WebGLRenderer.js 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968
  1. /**
  2. * @author supereggbert / http://www.paulbrunt.co.uk/
  3. * @author mrdoob / http://mrdoob.com/
  4. * @author alteredq / http://alteredqualia.com/
  5. */
  6. THREE.WebGLRenderer = function ( scene ) {
  7. // Currently you can use just up to 5 directional / point lights total.
  8. // Chrome barfs on shader linking when there are more than 5 lights :(
  9. // It seems problem comes from having too many varying vectors.
  10. // Weirdly, this is not GPU limitation as the same shader works ok in Firefox.
  11. // This difference could come from Chrome using ANGLE on Windows,
  12. // thus going DirectX9 route (while FF uses OpenGL).
  13. var _canvas = document.createElement( 'canvas' ), _gl, _program,
  14. _modelViewMatrix = new THREE.Matrix4(), _normalMatrix,
  15. COLORFILL = 0, COLORSTROKE = 1, BITMAP = 2, PHONG = 3, // material constants used in shader
  16. maxLightCount = allocateLights( scene, 5 );
  17. this.domElement = _canvas;
  18. this.autoClear = true;
  19. initGL();
  20. initProgram( maxLightCount.directional, maxLightCount.point );
  21. // Querying via gl.getParameter() reports different values for CH and FF for many max parameters.
  22. // On my GPU Chrome reports MAX_VARYING_VECTORS = 8, FF reports 0 yet compiles shaders with many
  23. // more varying vectors (up to 29 lights are ok, more start to throw warnings to FF error console
  24. // and then crash the browser).
  25. //alert( dumpObject( getGLParams() ) );
  26. function allocateLights( scene, maxLights ) {
  27. // heuristics to create shader parameters according to lights in the scene
  28. // (not to blow over maxLights budget)
  29. if ( scene ) {
  30. var l, ll, light, dirLights = pointLights = maxDirLights = maxPointLights = 0;
  31. for ( l = 0, ll = scene.lights.length; l < ll; l++ ) {
  32. light = scene.lights[ l ];
  33. if ( light instanceof THREE.DirectionalLight ) dirLights++;
  34. if ( light instanceof THREE.PointLight ) pointLights++;
  35. }
  36. if ( ( pointLights + dirLights ) <= maxLights ) {
  37. maxDirLights = dirLights;
  38. maxPointLights = pointLights;
  39. } else {
  40. maxDirLights = Math.ceil( maxLights * dirLights / ( pointLights + dirLights ) );
  41. maxPointLights = maxLights - maxDirLights;
  42. }
  43. return { 'directional' : maxDirLights, 'point' : maxPointLights };
  44. }
  45. return { 'directional' : 1, 'point' : maxLights - 1 };
  46. };
  47. this.setSize = function ( width, height ) {
  48. _canvas.width = width;
  49. _canvas.height = height;
  50. _gl.viewport( 0, 0, _canvas.width, _canvas.height );
  51. };
  52. this.clear = function () {
  53. _gl.clear( _gl.COLOR_BUFFER_BIT | _gl.DEPTH_BUFFER_BIT );
  54. };
  55. this.setupLights = function ( scene ) {
  56. var l, ll, light, r, g, b,
  57. ambientLights = [], pointLights = [], directionalLights = [],
  58. colors = [], positions = [];
  59. _gl.uniform1i( _program.enableLighting, scene.lights.length );
  60. for ( l = 0, ll = scene.lights.length; l < ll; l++ ) {
  61. light = scene.lights[ l ];
  62. if ( light instanceof THREE.AmbientLight ) {
  63. ambientLights.push( light );
  64. } else if ( light instanceof THREE.DirectionalLight ) {
  65. directionalLights.push( light );
  66. } else if( light instanceof THREE.PointLight ) {
  67. pointLights.push( light );
  68. }
  69. }
  70. // sum all ambient lights
  71. r = g = b = 0.0;
  72. for ( l = 0, ll = ambientLights.length; l < ll; l++ ) {
  73. r += ambientLights[ l ].color.r;
  74. g += ambientLights[ l ].color.g;
  75. b += ambientLights[ l ].color.b;
  76. }
  77. _gl.uniform3f( _program.ambientLightColor, r, g, b );
  78. // pass directional lights as float arrays
  79. colors = []; positions = [];
  80. for ( l = 0, ll = directionalLights.length; l < ll; l++ ) {
  81. light = directionalLights[ l ];
  82. colors.push( light.color.r * light.intensity );
  83. colors.push( light.color.g * light.intensity );
  84. colors.push( light.color.b * light.intensity );
  85. positions.push( light.position.x );
  86. positions.push( light.position.y );
  87. positions.push( light.position.z );
  88. }
  89. if ( directionalLights.length ) {
  90. _gl.uniform1i( _program.directionalLightNumber, directionalLights.length );
  91. _gl.uniform3fv( _program.directionalLightDirection, positions );
  92. _gl.uniform3fv( _program.directionalLightColor, colors );
  93. }
  94. // pass point lights as float arrays
  95. colors = []; positions = [];
  96. for ( l = 0, ll = pointLights.length; l < ll; l++ ) {
  97. light = pointLights[ l ];
  98. colors.push( light.color.r * light.intensity );
  99. colors.push( light.color.g * light.intensity );
  100. colors.push( light.color.b * light.intensity );
  101. positions.push( light.position.x );
  102. positions.push( light.position.y );
  103. positions.push( light.position.z );
  104. }
  105. if ( pointLights.length ) {
  106. _gl.uniform1i( _program.pointLightNumber, pointLights.length );
  107. _gl.uniform3fv( _program.pointLightPosition, positions );
  108. _gl.uniform3fv( _program.pointLightColor, colors );
  109. }
  110. };
  111. this.createBuffers = function ( object, mf ) {
  112. var f, fl, fi, face, vertexNormals, normal, uv, v1, v2, v3, v4,
  113. materialFaceGroup = object.materialFaceGroup[ mf ],
  114. faceArray = [],
  115. lineArray = [],
  116. vertexArray = [],
  117. normalArray = [],
  118. uvArray = [],
  119. vertexIndex = 0;
  120. for ( f = 0, fl = materialFaceGroup.faces.length; f < fl; f++ ) {
  121. fi = materialFaceGroup.faces[f];
  122. face = object.geometry.faces[ fi ];
  123. vertexNormals = face.vertexNormals;
  124. normal = face.normal;
  125. uv = object.geometry.uvs[ fi ];
  126. if ( face instanceof THREE.Face3 ) {
  127. v1 = object.geometry.vertices[ face.a ].position;
  128. v2 = object.geometry.vertices[ face.b ].position;
  129. v3 = object.geometry.vertices[ face.c ].position;
  130. vertexArray.push( v1.x, v1.y, v1.z );
  131. vertexArray.push( v2.x, v2.y, v2.z );
  132. vertexArray.push( v3.x, v3.y, v3.z );
  133. if ( vertexNormals.length == 3 ) {
  134. normalArray.push( vertexNormals[0].x, vertexNormals[0].y, vertexNormals[0].z );
  135. normalArray.push( vertexNormals[1].x, vertexNormals[1].y, vertexNormals[1].z );
  136. normalArray.push( vertexNormals[2].x, vertexNormals[2].y, vertexNormals[2].z );
  137. } else {
  138. normalArray.push( normal.x, normal.y, normal.z );
  139. normalArray.push( normal.x, normal.y, normal.z );
  140. normalArray.push( normal.x, normal.y, normal.z );
  141. }
  142. if ( uv ) {
  143. uvArray.push( uv[0].u, uv[0].v );
  144. uvArray.push( uv[1].u, uv[1].v );
  145. uvArray.push( uv[2].u, uv[2].v );
  146. }
  147. faceArray.push( vertexIndex, vertexIndex + 1, vertexIndex + 2 );
  148. // TODO: don't add lines that already exist (faces sharing edge)
  149. lineArray.push( vertexIndex, vertexIndex + 1 );
  150. lineArray.push( vertexIndex, vertexIndex + 2 );
  151. lineArray.push( vertexIndex + 1, vertexIndex + 2 );
  152. vertexIndex += 3;
  153. } else if ( face instanceof THREE.Face4 ) {
  154. v1 = object.geometry.vertices[ face.a ].position;
  155. v2 = object.geometry.vertices[ face.b ].position;
  156. v3 = object.geometry.vertices[ face.c ].position;
  157. v4 = object.geometry.vertices[ face.d ].position;
  158. vertexArray.push( v1.x, v1.y, v1.z );
  159. vertexArray.push( v2.x, v2.y, v2.z );
  160. vertexArray.push( v3.x, v3.y, v3.z );
  161. vertexArray.push( v4.x, v4.y, v4.z );
  162. if ( vertexNormals.length == 4 ) {
  163. normalArray.push( vertexNormals[0].x, vertexNormals[0].y, vertexNormals[0].z );
  164. normalArray.push( vertexNormals[1].x, vertexNormals[1].y, vertexNormals[1].z );
  165. normalArray.push( vertexNormals[2].x, vertexNormals[2].y, vertexNormals[2].z );
  166. normalArray.push( vertexNormals[3].x, vertexNormals[3].y, vertexNormals[3].z );
  167. } else {
  168. normalArray.push( normal.x, normal.y, normal.z );
  169. normalArray.push( normal.x, normal.y, normal.z );
  170. normalArray.push( normal.x, normal.y, normal.z );
  171. normalArray.push( normal.x, normal.y, normal.z );
  172. }
  173. if ( uv ) {
  174. uvArray.push( uv[0].u, uv[0].v );
  175. uvArray.push( uv[1].u, uv[1].v );
  176. uvArray.push( uv[2].u, uv[2].v );
  177. uvArray.push( uv[3].u, uv[3].v );
  178. }
  179. faceArray.push( vertexIndex, vertexIndex + 1, vertexIndex + 2 );
  180. faceArray.push( vertexIndex, vertexIndex + 2, vertexIndex + 3 );
  181. // TODO: don't add lines that already exist (faces sharing edge)
  182. lineArray.push( vertexIndex, vertexIndex + 1 );
  183. lineArray.push( vertexIndex, vertexIndex + 2 );
  184. lineArray.push( vertexIndex, vertexIndex + 3 );
  185. lineArray.push( vertexIndex + 1, vertexIndex + 2 );
  186. lineArray.push( vertexIndex + 2, vertexIndex + 3 );
  187. vertexIndex += 4;
  188. }
  189. }
  190. if ( !vertexArray.length ) {
  191. return;
  192. }
  193. materialFaceGroup.__webGLVertexBuffer = _gl.createBuffer();
  194. _gl.bindBuffer( _gl.ARRAY_BUFFER, materialFaceGroup.__webGLVertexBuffer );
  195. _gl.bufferData( _gl.ARRAY_BUFFER, new Float32Array( vertexArray ), _gl.STATIC_DRAW );
  196. materialFaceGroup.__webGLNormalBuffer = _gl.createBuffer();
  197. _gl.bindBuffer( _gl.ARRAY_BUFFER, materialFaceGroup.__webGLNormalBuffer );
  198. _gl.bufferData( _gl.ARRAY_BUFFER, new Float32Array( normalArray ), _gl.STATIC_DRAW );
  199. materialFaceGroup.__webGLUVBuffer = _gl.createBuffer();
  200. _gl.bindBuffer( _gl.ARRAY_BUFFER, materialFaceGroup.__webGLUVBuffer );
  201. _gl.bufferData( _gl.ARRAY_BUFFER, new Float32Array( uvArray ), _gl.STATIC_DRAW );
  202. materialFaceGroup.__webGLFaceBuffer = _gl.createBuffer();
  203. _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, materialFaceGroup.__webGLFaceBuffer );
  204. _gl.bufferData( _gl.ELEMENT_ARRAY_BUFFER, new Uint16Array( faceArray ), _gl.STATIC_DRAW );
  205. materialFaceGroup.__webGLLineBuffer = _gl.createBuffer();
  206. _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, materialFaceGroup.__webGLLineBuffer );
  207. _gl.bufferData( _gl.ELEMENT_ARRAY_BUFFER, new Uint16Array( lineArray ), _gl.STATIC_DRAW );
  208. materialFaceGroup.__webGLFaceCount = faceArray.length;
  209. materialFaceGroup.__webGLLineCount = lineArray.length;
  210. };
  211. this.renderBuffer = function ( material, materialFaceGroup ) {
  212. if ( material instanceof THREE.MeshPhongMaterial ) {
  213. mAmbient = material.ambient;
  214. mDiffuse = material.diffuse;
  215. mSpecular = material.specular;
  216. _gl.uniform4f( _program.mAmbient, mAmbient.r, mAmbient.g, mAmbient.b, material.opacity );
  217. _gl.uniform4f( _program.mDiffuse, mDiffuse.r, mDiffuse.g, mDiffuse.b, material.opacity );
  218. _gl.uniform4f( _program.mSpecular, mSpecular.r, mSpecular.g, mSpecular.b, material.opacity );
  219. _gl.uniform1f( _program.mShininess, material.shininess );
  220. _gl.uniform1i( _program.material, PHONG );
  221. } else if ( material instanceof THREE.MeshColorFillMaterial ) {
  222. color = material.color;
  223. _gl.uniform4f( _program.mColor, color.r * color.a, color.g * color.a, color.b * color.a, color.a );
  224. _gl.uniform1i( _program.material, COLORFILL );
  225. } else if ( material instanceof THREE.MeshColorStrokeMaterial ) {
  226. lineWidth = material.lineWidth;
  227. color = material.color;
  228. _gl.uniform4f( _program.mColor, color.r * color.a, color.g * color.a, color.b * color.a, color.a );
  229. _gl.uniform1i( _program.material, COLORSTROKE );
  230. } else if ( material instanceof THREE.MeshBitmapMaterial ) {
  231. if ( !material.__webGLTexture && material.loaded ) {
  232. material.__webGLTexture = _gl.createTexture();
  233. _gl.bindTexture( _gl.TEXTURE_2D, material.__webGLTexture );
  234. _gl.texImage2D( _gl.TEXTURE_2D, 0, _gl.RGBA, _gl.RGBA, _gl.UNSIGNED_BYTE, material.bitmap ) ;
  235. _gl.texParameteri( _gl.TEXTURE_2D, _gl.TEXTURE_MAG_FILTER, _gl.LINEAR );
  236. //_gl.texParameteri( _gl.TEXTURE_2D, _gl.TEXTURE_MIN_FILTER, _gl.LINEAR_MIPMAP_NEAREST );
  237. _gl.texParameteri( _gl.TEXTURE_2D, _gl.TEXTURE_MIN_FILTER, _gl.LINEAR_MIPMAP_LINEAR );
  238. _gl.generateMipmap( _gl.TEXTURE_2D );
  239. _gl.bindTexture( _gl.TEXTURE_2D, null );
  240. }
  241. _gl.activeTexture( _gl.TEXTURE0 );
  242. _gl.bindTexture( _gl.TEXTURE_2D, material.__webGLTexture );
  243. _gl.uniform1i( _program.tDiffuse, 0 );
  244. _gl.uniform1i( _program.material, BITMAP );
  245. }
  246. // vertices
  247. _gl.bindBuffer( _gl.ARRAY_BUFFER, materialFaceGroup.__webGLVertexBuffer );
  248. _gl.vertexAttribPointer( _program.position, 3, _gl.FLOAT, false, 0, 0 );
  249. // normals
  250. _gl.bindBuffer( _gl.ARRAY_BUFFER, materialFaceGroup.__webGLNormalBuffer );
  251. _gl.vertexAttribPointer( _program.normal, 3, _gl.FLOAT, false, 0, 0 );
  252. // uvs
  253. if ( material instanceof THREE.MeshBitmapMaterial ) {
  254. _gl.bindBuffer( _gl.ARRAY_BUFFER, materialFaceGroup.__webGLUVBuffer );
  255. _gl.enableVertexAttribArray( _program.uv );
  256. _gl.vertexAttribPointer( _program.uv, 2, _gl.FLOAT, false, 0, 0 );
  257. } else {
  258. _gl.disableVertexAttribArray( _program.uv );
  259. }
  260. // render triangles
  261. if ( material instanceof THREE.MeshBitmapMaterial ||
  262. material instanceof THREE.MeshColorFillMaterial ||
  263. material instanceof THREE.MeshPhongMaterial ) {
  264. _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, materialFaceGroup.__webGLFaceBuffer );
  265. _gl.drawElements( _gl.TRIANGLES, materialFaceGroup.__webGLFaceCount, _gl.UNSIGNED_SHORT, 0 );
  266. // render lines
  267. } else if ( material instanceof THREE.MeshColorStrokeMaterial ) {
  268. _gl.lineWidth( lineWidth );
  269. _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, materialFaceGroup.__webGLLineBuffer );
  270. _gl.drawElements( _gl.LINES, materialFaceGroup.__webGLLineCount, _gl.UNSIGNED_SHORT, 0 );
  271. }
  272. };
  273. this.renderMesh = function ( object, camera ) {
  274. var i, l, m, ml, mf, material, meshMaterial, materialFaceGroup;
  275. // create separate VBOs per material
  276. for ( mf in object.materialFaceGroup ) {
  277. materialFaceGroup = object.materialFaceGroup[ mf ];
  278. // initialise buffers on the first access
  279. if( ! materialFaceGroup.__webGLVertexBuffer ) {
  280. this.createBuffers( object, mf );
  281. }
  282. for ( m = 0, ml = object.material.length; m < ml; m++ ) {
  283. meshMaterial = object.material[ m ];
  284. if ( meshMaterial instanceof THREE.MeshFaceMaterial ) {
  285. for ( i = 0, l = materialFaceGroup.material.length; i < l; i++ ) {
  286. material = materialFaceGroup.material[ i ];
  287. this.renderBuffer( material, materialFaceGroup );
  288. }
  289. } else {
  290. material = meshMaterial;
  291. this.renderBuffer( material, materialFaceGroup );
  292. }
  293. }
  294. }
  295. };
  296. this.setupMatrices = function ( object, camera ) {
  297. object.autoUpdateMatrix && object.updateMatrix();
  298. _modelViewMatrix.multiply( camera.matrix, object.matrix );
  299. _program.viewMatrixArray = new Float32Array( camera.matrix.flatten() );
  300. _program.modelViewMatrixArray = new Float32Array( _modelViewMatrix.flatten() );
  301. _program.projectionMatrixArray = new Float32Array( camera.projectionMatrix.flatten() );
  302. _normalMatrix = THREE.Matrix4.makeInvert3x3( _modelViewMatrix ).transpose();
  303. _program.normalMatrixArray = new Float32Array( _normalMatrix.m );
  304. _gl.uniformMatrix4fv( _program.viewMatrix, false, _program.viewMatrixArray );
  305. _gl.uniformMatrix4fv( _program.modelViewMatrix, false, _program.modelViewMatrixArray );
  306. _gl.uniformMatrix4fv( _program.projectionMatrix, false, _program.projectionMatrixArray );
  307. _gl.uniformMatrix3fv( _program.normalMatrix, false, _program.normalMatrixArray );
  308. _gl.uniformMatrix4fv( _program.objMatrix, false, new Float32Array( object.matrix.flatten() ) );
  309. };
  310. this.render = function ( scene, camera ) {
  311. var o, ol, object;
  312. if ( this.autoClear ) {
  313. this.clear();
  314. }
  315. camera.autoUpdateMatrix && camera.updateMatrix();
  316. _gl.uniform3f( _program.cameraPosition, camera.position.x, camera.position.y, camera.position.z );
  317. this.setupLights( scene );
  318. for ( o = 0, ol = scene.objects.length; o < ol; o++ ) {
  319. object = scene.objects[ o ];
  320. this.setupMatrices( object, camera );
  321. if ( object instanceof THREE.Mesh ) {
  322. this.renderMesh( object, camera );
  323. } else if ( object instanceof THREE.Line ) {
  324. // TODO
  325. // It would be very inefficient to do lines one-by-one.
  326. // This will need a complete redesign from how CanvasRenderer does it.
  327. // Though it could be brute forced, if only used for lightweight
  328. // stuff (as CanvasRenderer can only handle small number of elements
  329. // anyways).
  330. // Heavy-duty wireframe lines are handled efficiently in mesh renderer.
  331. } else if ( object instanceof THREE.Particle ) {
  332. // TODO
  333. // The same as with lines, particles shouldn't be handled one-by-one.
  334. // Again, heavy duty particle system would require different approach,
  335. // like one VBO per particle system and then update attribute arrays,
  336. // though the best would be to move also behavior computation
  337. // into the shader (ala http://spidergl.org/example.php?id=11)
  338. }
  339. }
  340. };
  341. function initGL() {
  342. try {
  343. _gl = _canvas.getContext( 'experimental-webgl', { antialias: true} );
  344. } catch(e) { }
  345. if (!_gl) {
  346. alert("WebGL not supported");
  347. throw "cannot create webgl context";
  348. }
  349. _gl.clearColor( 0, 0, 0, 1 );
  350. _gl.clearDepth( 1 );
  351. _gl.enable( _gl.DEPTH_TEST );
  352. _gl.depthFunc( _gl.LEQUAL );
  353. _gl.enable( _gl.BLEND );
  354. //_gl.blendFunc( _gl.SRC_ALPHA, _gl.ONE_MINUS_SRC_ALPHA );
  355. // _gl.blendFunc( _gl.SRC_ALPHA, _gl.ONE ); // cool!
  356. _gl.blendFunc( _gl.ONE, _gl.ONE_MINUS_SRC_ALPHA );
  357. _gl.clearColor( 0, 0, 0, 0 );
  358. };
  359. function generateFragmentShader( maxDirLights, maxPointLights ) {
  360. var chunks = [
  361. "#ifdef GL_ES",
  362. "precision highp float;",
  363. "#endif",
  364. maxDirLights ? "#define MAX_DIR_LIGHTS " + maxDirLights : "",
  365. maxPointLights ? "#define MAX_POINT_LIGHTS " + maxPointLights : "",
  366. "uniform int material;", // 0 - ColorFill, 1 - ColorStroke, 2 - Bitmap, 3 - Phong
  367. "uniform sampler2D tDiffuse;",
  368. "uniform vec4 mColor;",
  369. "uniform vec4 mAmbient;",
  370. "uniform vec4 mDiffuse;",
  371. "uniform vec4 mSpecular;",
  372. "uniform float mShininess;",
  373. "uniform int pointLightNumber;",
  374. "uniform int directionalLightNumber;",
  375. maxDirLights ? "uniform mat4 viewMatrix;" : "",
  376. maxDirLights ? "uniform vec3 directionalLightDirection[ MAX_DIR_LIGHTS ];" : "",
  377. "varying vec3 vNormal;",
  378. "varying vec2 vUv;",
  379. "varying vec3 vLightWeighting;",
  380. maxPointLights ? "varying vec3 vPointLightVector[ MAX_POINT_LIGHTS ];" : "",
  381. "varying vec3 vViewPosition;",
  382. "void main() {",
  383. // Blinn-Phong
  384. // based on o3d example
  385. "if ( material == 3 ) { ",
  386. "vec3 normal = normalize( vNormal );",
  387. "vec3 viewPosition = normalize( vViewPosition );",
  388. // point lights
  389. maxPointLights ? "vec4 pointDiffuse = vec4( 0.0, 0.0, 0.0, 0.0 );" : "",
  390. maxPointLights ? "vec4 pointSpecular = vec4( 0.0, 0.0, 0.0, 0.0 );" : "",
  391. maxPointLights ? "for( int i = 0; i < pointLightNumber; i++ ) {" : "",
  392. maxPointLights ? "vec3 pointVector = normalize( vPointLightVector[ i ] );" : "",
  393. maxPointLights ? "vec3 pointHalfVector = normalize( vPointLightVector[ i ] + vViewPosition );" : "",
  394. maxPointLights ? "float pointDotNormalHalf = dot( normal, pointHalfVector );" : "",
  395. maxPointLights ? "float pointDiffuseWeight = max( dot( normal, pointVector ), 0.0 );" : "",
  396. // Ternary conditional is from the original o3d shader. Here it produces abrupt dark cutoff artefacts.
  397. // Using just pow works ok in Chrome, but makes different artefact in Firefox 4.
  398. // Zeroing on negative pointDotNormalHalf seems to work in both.
  399. //"float specularCompPoint = dot( normal, pointVector ) < 0.0 || pointDotNormalHalf < 0.0 ? 0.0 : pow( pointDotNormalHalf, mShininess );",
  400. //"float specularCompPoint = pow( pointDotNormalHalf, mShininess );",
  401. //"float pointSpecularWeight = pointDotNormalHalf < 0.0 ? 0.0 : pow( pointDotNormalHalf, mShininess );",
  402. // Ternary conditional inside for loop breaks Chrome shader linking.
  403. // Must do it with if.
  404. maxPointLights ? "float pointSpecularWeight = 0.0;" : "",
  405. maxPointLights ? "if ( pointDotNormalHalf >= 0.0 )" : "",
  406. maxPointLights ? "pointSpecularWeight = pow( pointDotNormalHalf, mShininess );" : "",
  407. maxPointLights ? "pointDiffuse += mDiffuse * pointDiffuseWeight;" : "",
  408. maxPointLights ? "pointSpecular += mSpecular * pointSpecularWeight;" : "",
  409. maxPointLights ? "}" : "",
  410. // directional lights
  411. maxDirLights ? "vec4 dirDiffuse = vec4( 0.0, 0.0, 0.0, 0.0 );" : "",
  412. maxDirLights ? "vec4 dirSpecular = vec4( 0.0, 0.0, 0.0, 0.0 );" : "",
  413. maxDirLights ? "for( int i = 0; i < directionalLightNumber; i++ ) {" : "",
  414. maxDirLights ? "vec4 lDirection = viewMatrix * vec4( directionalLightDirection[ i ], 0.0 );" : "",
  415. maxDirLights ? "vec3 dirVector = normalize( lDirection.xyz );" : "",
  416. maxDirLights ? "vec3 dirHalfVector = normalize( lDirection.xyz + vViewPosition );" : "",
  417. maxDirLights ? "float dirDotNormalHalf = dot( normal, dirHalfVector );" : "",
  418. maxDirLights ? "float dirDiffuseWeight = max( dot( normal, dirVector ), 0.0 );" : "",
  419. maxDirLights ? "float dirSpecularWeight = 0.0;" : "",
  420. maxDirLights ? "if ( dirDotNormalHalf >= 0.0 )" : "",
  421. maxDirLights ? "dirSpecularWeight = pow( dirDotNormalHalf, mShininess );" : "",
  422. maxDirLights ? "dirDiffuse += mDiffuse * dirDiffuseWeight;" : "",
  423. maxDirLights ? "dirSpecular += mSpecular * dirSpecularWeight;" : "",
  424. maxDirLights ? "}" : "",
  425. // all lights contribution summation
  426. "vec4 totalLight = mAmbient;",
  427. maxDirLights ? "totalLight += dirDiffuse + dirSpecular;" : "",
  428. maxPointLights ? "totalLight += pointDiffuse + pointSpecular;" : "",
  429. // looks nicer with weighting
  430. "gl_FragColor = vec4( totalLight.xyz * vLightWeighting, 1.0 );",
  431. //"gl_FragColor = vec4( totalLight.xyz, 1.0 );",
  432. // Bitmap: texture
  433. "} else if ( material == 2 ) {",
  434. "vec4 texelColor = texture2D( tDiffuse, vUv );",
  435. "gl_FragColor = vec4( texelColor.rgb * vLightWeighting, texelColor.a );",
  436. // ColorStroke: wireframe using uniform color
  437. "} else if ( material == 1 ) {",
  438. "gl_FragColor = vec4( mColor.rgb * vLightWeighting, mColor.a );",
  439. // ColorFill: triangle using uniform color
  440. "} else {",
  441. "gl_FragColor = vec4( mColor.rgb * vLightWeighting, mColor.a );",
  442. "}",
  443. "}" ];
  444. return chunks.join("\n");
  445. };
  446. function generateVertexShader( maxDirLights, maxPointLights ) {
  447. var chunks = [
  448. maxDirLights ? "#define MAX_DIR_LIGHTS " + maxDirLights : "",
  449. maxPointLights ? "#define MAX_POINT_LIGHTS " + maxPointLights : "",
  450. "attribute vec3 position;",
  451. "attribute vec3 normal;",
  452. "attribute vec2 uv;",
  453. "uniform vec3 cameraPosition;",
  454. "uniform bool enableLighting;",
  455. "uniform int pointLightNumber;",
  456. "uniform int directionalLightNumber;",
  457. "uniform vec3 ambientLightColor;",
  458. maxDirLights ? "uniform vec3 directionalLightColor[ MAX_DIR_LIGHTS ];" : "",
  459. maxDirLights ? "uniform vec3 directionalLightDirection[ MAX_DIR_LIGHTS ];" : "",
  460. maxPointLights ? "uniform vec3 pointLightColor[ MAX_POINT_LIGHTS ];" : "",
  461. maxPointLights ? "uniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];" : "",
  462. "uniform mat4 objMatrix;",
  463. "uniform mat4 viewMatrix;",
  464. "uniform mat4 modelViewMatrix;",
  465. "uniform mat4 projectionMatrix;",
  466. "uniform mat3 normalMatrix;",
  467. "varying vec3 vNormal;",
  468. "varying vec2 vUv;",
  469. "varying vec3 vLightWeighting;",
  470. maxPointLights ? "varying vec3 vPointLightVector[ MAX_POINT_LIGHTS ];" : "",
  471. "varying vec3 vViewPosition;",
  472. "void main(void) {",
  473. // world space
  474. "vec4 mPosition = objMatrix * vec4( position, 1.0 );",
  475. "vViewPosition = cameraPosition - mPosition.xyz;",
  476. // eye space
  477. "vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );",
  478. "vec3 transformedNormal = normalize( normalMatrix * normal );",
  479. "if ( !enableLighting ) {",
  480. "vLightWeighting = vec3( 1.0, 1.0, 1.0 );",
  481. "} else {",
  482. "vLightWeighting = ambientLightColor;",
  483. // directional lights
  484. maxDirLights ? "for( int i = 0; i < directionalLightNumber; i++ ) {" : "",
  485. maxDirLights ? "vec4 lDirection = viewMatrix * vec4( directionalLightDirection[ i ], 0.0 );" : "",
  486. maxDirLights ? "float directionalLightWeighting = max( dot( transformedNormal, normalize(lDirection.xyz ) ), 0.0 );" : "",
  487. maxDirLights ? "vLightWeighting += directionalLightColor[ i ] * directionalLightWeighting;" : "",
  488. maxDirLights ? "}" : "",
  489. // point lights
  490. maxPointLights ? "for( int i = 0; i < pointLightNumber; i++ ) {" : "",
  491. maxPointLights ? "vec4 lPosition = viewMatrix * vec4( pointLightPosition[ i ], 1.0 );" : "",
  492. maxPointLights ? "vPointLightVector[ i ] = normalize( lPosition.xyz - mvPosition.xyz );" : "",
  493. maxPointLights ? "float pointLightWeighting = max( dot( transformedNormal, vPointLightVector[ i ] ), 0.0 );" : "",
  494. maxPointLights ? "vLightWeighting += pointLightColor[ i ] * pointLightWeighting;" : "",
  495. maxPointLights ? "}" : "",
  496. "}",
  497. "vNormal = transformedNormal;",
  498. "vUv = uv;",
  499. "gl_Position = projectionMatrix * mvPosition;",
  500. "}" ];
  501. return chunks.join("\n");
  502. };
  503. function initProgram( maxDirLights, maxPointLights ) {
  504. _program = _gl.createProgram();
  505. //log ( generateVertexShader( maxDirLights, maxPointLights ) );
  506. //log ( generateFragmentShader( maxDirLights, maxPointLights ) );
  507. _gl.attachShader( _program, getShader( "fragment", generateFragmentShader( maxDirLights, maxPointLights ) ) );
  508. _gl.attachShader( _program, getShader( "vertex", generateVertexShader( maxDirLights, maxPointLights ) ) );
  509. _gl.linkProgram( _program );
  510. if ( !_gl.getProgramParameter( _program, _gl.LINK_STATUS ) ) {
  511. alert( "Could not initialise shaders" );
  512. //alert( "VALIDATE_STATUS: " + _gl.getProgramParameter( _program, _gl.VALIDATE_STATUS ) );
  513. //alert( _gl.getError() );
  514. }
  515. _gl.useProgram( _program );
  516. // matrices
  517. _program.viewMatrix = _gl.getUniformLocation( _program, "viewMatrix" );
  518. _program.modelViewMatrix = _gl.getUniformLocation( _program, "modelViewMatrix" );
  519. _program.projectionMatrix = _gl.getUniformLocation( _program, "projectionMatrix" );
  520. _program.normalMatrix = _gl.getUniformLocation( _program, "normalMatrix" );
  521. _program.objMatrix = _gl.getUniformLocation( _program, "objMatrix" );
  522. _program.cameraPosition = _gl.getUniformLocation(_program, 'cameraPosition');
  523. // lights
  524. _program.enableLighting = _gl.getUniformLocation(_program, 'enableLighting');
  525. _program.ambientLightColor = _gl.getUniformLocation(_program, 'ambientLightColor');
  526. if ( maxDirLights ) {
  527. _program.directionalLightNumber = _gl.getUniformLocation(_program, 'directionalLightNumber');
  528. _program.directionalLightColor = _gl.getUniformLocation(_program, 'directionalLightColor');
  529. _program.directionalLightDirection = _gl.getUniformLocation(_program, 'directionalLightDirection');
  530. }
  531. if ( maxPointLights ) {
  532. _program.pointLightNumber = _gl.getUniformLocation(_program, 'pointLightNumber');
  533. _program.pointLightColor = _gl.getUniformLocation(_program, 'pointLightColor');
  534. _program.pointLightPosition = _gl.getUniformLocation(_program, 'pointLightPosition');
  535. }
  536. // material
  537. _program.material = _gl.getUniformLocation(_program, 'material');
  538. // material properties (ColorFill / ColorStroke shader)
  539. _program.mColor = _gl.getUniformLocation(_program, 'mColor');
  540. // material properties (Blinn-Phong shader)
  541. _program.mAmbient = _gl.getUniformLocation(_program, 'mAmbient');
  542. _program.mDiffuse = _gl.getUniformLocation(_program, 'mDiffuse');
  543. _program.mSpecular = _gl.getUniformLocation(_program, 'mSpecular');
  544. _program.mShininess = _gl.getUniformLocation(_program, 'mShininess');
  545. // texture (Bitmap shader)
  546. _program.tDiffuse = _gl.getUniformLocation( _program, "tDiffuse");
  547. _gl.uniform1i( _program.tDiffuse, 0 );
  548. // vertex arrays
  549. _program.position = _gl.getAttribLocation( _program, "position" );
  550. _gl.enableVertexAttribArray( _program.position );
  551. _program.normal = _gl.getAttribLocation( _program, "normal" );
  552. _gl.enableVertexAttribArray( _program.normal );
  553. _program.uv = _gl.getAttribLocation( _program, "uv" );
  554. _gl.enableVertexAttribArray( _program.uv );
  555. _program.viewMatrixArray = new Float32Array(16);
  556. _program.modelViewMatrixArray = new Float32Array(16);
  557. _program.projectionMatrixArray = new Float32Array(16);
  558. };
  559. function getShader( type, string ) {
  560. var shader;
  561. if ( type == "fragment" ) {
  562. shader = _gl.createShader( _gl.FRAGMENT_SHADER );
  563. } else if ( type == "vertex" ) {
  564. shader = _gl.createShader( _gl.VERTEX_SHADER );
  565. }
  566. _gl.shaderSource( shader, string );
  567. _gl.compileShader( shader );
  568. if ( !_gl.getShaderParameter( shader, _gl.COMPILE_STATUS ) ) {
  569. alert( _gl.getShaderInfoLog( shader ) );
  570. return null;
  571. }
  572. return shader;
  573. };
  574. function getGLParams() {
  575. var params = {
  576. 'MAX_VARYING_VECTORS': _gl.getParameter( _gl.MAX_VARYING_VECTORS ),
  577. 'MAX_VERTEX_ATTRIBS': _gl.getParameter( _gl.MAX_VERTEX_ATTRIBS ),
  578. 'MAX_TEXTURE_IMAGE_UNITS': _gl.getParameter( _gl.MAX_TEXTURE_IMAGE_UNITS ),
  579. 'MAX_VERTEX_TEXTURE_IMAGE_UNITS': _gl.getParameter( _gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS ),
  580. 'MAX_COMBINED_TEXTURE_IMAGE_UNITS' : _gl.getParameter( _gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS ),
  581. 'MAX_VERTEX_UNIFORM_VECTORS': _gl.getParameter( _gl.MAX_VERTEX_UNIFORM_VECTORS ),
  582. 'MAX_FRAGMENT_UNIFORM_VECTORS': _gl.getParameter( _gl.MAX_FRAGMENT_UNIFORM_VECTORS )
  583. }
  584. return params;
  585. };
  586. function dumpObject( obj ) {
  587. var p, str = "";
  588. for ( p in obj ) {
  589. str += p + ": " + obj[p] + "\n";
  590. }
  591. return str;
  592. }
  593. };