WebGLRenderer3.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656
  1. /**
  2. * @author mrdoob / http://mrdoob.com/
  3. *
  4. * parameters = {
  5. * canvas: canvas,
  6. * contextAttributes: {
  7. * alpha: true,
  8. * depth: true,
  9. * stencil: false,
  10. * antialias: true,
  11. * premultipliedAlpha: true,
  12. * preserveDrawingBuffer: false
  13. * }
  14. * }
  15. *
  16. */
  17. THREE.WebGLRenderer3 = function ( parameters ) {
  18. console.log( 'THREE.WebGLRenderer3', THREE.REVISION );
  19. parameters = parameters || {};
  20. var scope = this;
  21. var canvas = parameters.canvas !== undefined ? parameters.canvas : document.createElement( 'canvas' );
  22. var devicePixelRatio = parameters.devicePixelRatio !== undefined
  23. ? parameters.devicePixelRatio
  24. : window.devicePixelRatio !== undefined
  25. ? window.devicePixelRatio
  26. : 1;
  27. var gl;
  28. try {
  29. var attributes = parameters.contextAttributes || {};
  30. gl = canvas.getContext( 'webgl', attributes ) || canvas.getContext( 'experimental-webgl', attributes );
  31. } catch ( exception ) {
  32. console.error( exception );
  33. }
  34. var precision = 'highp';
  35. var extensions = {};
  36. if ( gl !== null ) {
  37. extensions.element_index_uint = gl.getExtension( 'OES_element_index_uint' );
  38. extensions.texture_float = gl.getExtension( 'OES_texture_float' );
  39. extensions.texture_float_linear = gl.getExtension( 'OES_texture_float_linear' );
  40. extensions.standard_derivatives = gl.getExtension( 'OES_standard_derivatives' );
  41. extensions.texture_filter_anisotropic = gl.getExtension( 'EXT_texture_filter_anisotropic' ) || gl.getExtension( 'MOZ_EXT_texture_filter_anisotropic' ) || gl.getExtension( 'WEBKIT_EXT_texture_filter_anisotropic' );
  42. extensions.compressed_texture_s3tc = gl.getExtension( 'WEBGL_compressed_texture_s3tc' ) || gl.getExtension( 'MOZ_WEBGL_compressed_texture_s3tc' ) || gl.getExtension( 'WEBKIT_WEBGL_compressed_texture_s3tc' );
  43. gl.clearColor( 0, 0, 0, 1 );
  44. gl.clearDepth( 1 );
  45. gl.clearStencil( 0 );
  46. gl.enable( gl.DEPTH_TEST );
  47. gl.depthFunc( gl.LEQUAL );
  48. gl.enable( gl.CULL_FACE );
  49. gl.frontFace( gl.CCW );
  50. gl.cullFace( gl.BACK );
  51. gl.enable( gl.BLEND );
  52. gl.blendEquation( gl.FUNC_ADD );
  53. gl.blendFunc( gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA );
  54. gl.clearColor( 0, 0, 0, 0 );
  55. }
  56. var clearColor = new THREE.Color( 0x000000 );
  57. var clearAlpha = 0;
  58. //
  59. var vector3 = new THREE.Vector3();
  60. var frustum = new THREE.Frustum();
  61. var normalMatrix = new THREE.Matrix3();
  62. var modelViewMatrix = new THREE.Matrix4();
  63. var cameraViewProjectionMatrix = new THREE.Matrix4();
  64. // buffers
  65. var buffers = {};
  66. var getBuffer = function ( geometry, material ) {
  67. var hash = geometry.id.toString() + '+' + material.id.toString();
  68. if ( buffers[ hash ] !== undefined ) {
  69. return buffers[ hash ];
  70. }
  71. var vertices = geometry.vertices;
  72. var faces = geometry.faces;
  73. //
  74. var positions = [];
  75. var addPosition = function ( position ) {
  76. positions.push( position.x, position.y, position.z );
  77. }
  78. var normals = [];
  79. var addNormal = function ( normal ) {
  80. normals.push( normal.x, normal.y, normal.z );
  81. }
  82. for ( var i = 0, l = faces.length; i < l; i ++ ) {
  83. var face = faces[ i ];
  84. var vertexNormals = face.vertexNormals.length > 0;
  85. addPosition( vertices[ face.a ] );
  86. addPosition( vertices[ face.b ] );
  87. addPosition( vertices[ face.c ] );
  88. if ( vertexNormals === true ) {
  89. addNormal( face.vertexNormals[ 0 ] );
  90. addNormal( face.vertexNormals[ 1 ] );
  91. addNormal( face.vertexNormals[ 2 ] );
  92. } else {
  93. addNormal( face.normal );
  94. addNormal( face.normal );
  95. addNormal( face.normal );
  96. }
  97. if ( face instanceof THREE.Face4 ) {
  98. addPosition( vertices[ face.a ] );
  99. addPosition( vertices[ face.c ] );
  100. addPosition( vertices[ face.d ] );
  101. if ( vertexNormals === true ) {
  102. addNormal( face.vertexNormals[ 0 ] );
  103. addNormal( face.vertexNormals[ 2 ] );
  104. addNormal( face.vertexNormals[ 3 ] );
  105. } else {
  106. addNormal( face.normal );
  107. addNormal( face.normal );
  108. addNormal( face.normal );
  109. }
  110. }
  111. }
  112. var buffer = {
  113. positions: gl.createBuffer(),
  114. normals: gl.createBuffer(),
  115. count: positions.length / 3
  116. };
  117. gl.bindBuffer( gl.ARRAY_BUFFER, buffer.positions );
  118. gl.bufferData( gl.ARRAY_BUFFER, new Float32Array( positions ), gl.STATIC_DRAW );
  119. gl.bindBuffer( gl.ARRAY_BUFFER, buffer.normals );
  120. gl.bufferData( gl.ARRAY_BUFFER, new Float32Array( normals ), gl.STATIC_DRAW );
  121. buffers[ hash ] = buffer;
  122. scope.info.memory.geometries ++;
  123. return buffer;
  124. };
  125. // programs
  126. var programs = {};
  127. var programsCache = {};
  128. var getProgram = function ( material ) {
  129. if ( programs[ material.id ] !== undefined ) {
  130. return programs[ material.id ];
  131. }
  132. var vertexShader = [
  133. 'precision ' + precision + ' float;',
  134. 'attribute vec3 position;',
  135. 'attribute vec3 normal;',
  136. 'uniform mat4 modelViewMatrix;',
  137. 'uniform mat3 normalMatrix;',
  138. 'uniform mat4 projectionMatrix;',
  139. ''
  140. ].join( '\n' );
  141. var fragmentShader = [
  142. 'precision ' + precision + ' float;',
  143. ''
  144. ].join( '\n' );
  145. if ( material instanceof THREE.ShaderMaterial ) {
  146. vertexShader += material.vertexShader;
  147. fragmentShader += material.fragmentShader;
  148. } else if ( material instanceof THREE.MeshNormalMaterial ) {
  149. vertexShader += [
  150. 'varying vec3 vNormal;',
  151. 'void main() {',
  152. ' vNormal = normalize( normalMatrix * normal );',
  153. ' gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );',
  154. '}'
  155. ].join( '\n' );
  156. fragmentShader += [
  157. 'varying vec3 vNormal;',
  158. 'uniform float opacity;',
  159. 'void main() {',
  160. ' gl_FragColor = vec4( 0.5 * normalize( vNormal ) + 0.5, opacity );',
  161. '}'
  162. ].join( '\n' );
  163. } else {
  164. vertexShader += [
  165. 'void main() {',
  166. ' gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );',
  167. '}'
  168. ].join( '\n' );
  169. fragmentShader += [
  170. 'void main() {',
  171. ' gl_FragColor = vec4( 1.0, 0, 0, 1.0 );',
  172. '}'
  173. ].join( '\n' );
  174. }
  175. var program;
  176. var code = vertexShader + fragmentShader;
  177. if ( programsCache[ code ] !== undefined ) {
  178. program = programsCache[ code ];
  179. programs[ material.id ] = program;
  180. } else {
  181. program = gl.createProgram();
  182. gl.attachShader( program, createShader( gl.VERTEX_SHADER, vertexShader ) );
  183. gl.attachShader( program, createShader( gl.FRAGMENT_SHADER, fragmentShader ) );
  184. gl.linkProgram( program );
  185. if ( gl.getProgramParameter( program, gl.LINK_STATUS ) === true ) {
  186. programsCache[ code ] = program;
  187. programs[ material.id ] = program;
  188. scope.info.memory.programs ++;
  189. } else {
  190. console.error( 'VALIDATE_STATUS: ' + gl.getProgramParameter( program, gl.VALIDATE_STATUS ) );
  191. console.error( 'GL_ERROR: ' + gl.getError() );
  192. // fallback
  193. program = getProgram( new THREE.MeshBasicMaterial() );
  194. programs[ material.id ] = program;
  195. }
  196. }
  197. return program;
  198. };
  199. var createShader = function ( type, string ) {
  200. var shader = gl.createShader( type );
  201. gl.shaderSource( shader, string );
  202. gl.compileShader( shader );
  203. if ( gl.getShaderParameter( shader, gl.COMPILE_STATUS ) === true ) {
  204. // console.log( string );
  205. } else {
  206. console.error( gl.getShaderInfoLog( shader ), string );
  207. return null;
  208. }
  209. return shader;
  210. };
  211. this.info = {
  212. memory: {
  213. programs: 0,
  214. geometries: 0,
  215. textures: 0
  216. },
  217. render: {
  218. calls: 0,
  219. vertices: 0,
  220. faces: 0,
  221. points: 0
  222. }
  223. };
  224. this.domElement = canvas;
  225. this.extensions = extensions;
  226. this.autoClear = true; // TODO: Make private
  227. this.setClearColor = function ( color, alpha ) {
  228. clearColor.set( color );
  229. clearAlpha = alpha !== undefined ? alpha : 1;
  230. gl.clearColor( clearColor.r, clearColor.g, clearColor.b, clearAlpha );
  231. };
  232. this.setSize = function ( width, height, updateStyle ) {
  233. canvas.width = width * devicePixelRatio;
  234. canvas.height = height * devicePixelRatio;
  235. if ( devicePixelRatio !== 1 && updateStyle !== false ) {
  236. canvas.style.width = width + 'px';
  237. canvas.style.height = height + 'px';
  238. }
  239. gl.viewport( 0, 0, canvas.width, canvas.height );
  240. };
  241. this.clear = function ( color, depth, stencil ) {
  242. var bits = 0;
  243. if ( color === undefined || color ) bits |= gl.COLOR_BUFFER_BIT;
  244. if ( depth === undefined || depth ) bits |= gl.DEPTH_BUFFER_BIT;
  245. if ( stencil === undefined || stencil ) bits |= gl.STENCIL_BUFFER_BIT;
  246. gl.clear( bits );
  247. };
  248. // blending
  249. var currentBlending = null;
  250. var setBlending = function ( blending ) {
  251. if ( blending !== currentBlending ) {
  252. if ( blending === THREE.NoBlending ) {
  253. gl.disable( gl.BLEND );
  254. } else {
  255. gl.enable( gl.BLEND );
  256. }
  257. currentBlending = blending;
  258. }
  259. };
  260. // depthTest
  261. var currentDepthTest = null;
  262. var setDepthTest = function ( value ) {
  263. if ( value !== currentDepthTest ) {
  264. value === true ? gl.enable( gl.DEPTH_TEST ) : gl.disable( gl.DEPTH_TEST );
  265. currentDepthTest = value;
  266. }
  267. };
  268. // depthWrite
  269. var currentDepthWrite = null;
  270. var setDepthWrite = function ( value ) {
  271. if ( value !== currentDepthWrite ) {
  272. gl.depthMask( value );
  273. currentDepthWrite = value;
  274. }
  275. };
  276. var objectsOpaque = [];
  277. var objectsTransparent = [];
  278. var projectObject = function ( object, camera ) {
  279. if ( object.visible === false ) return;
  280. if ( object instanceof THREE.Mesh && frustum.intersectsObject( object ) === true ) {
  281. // TODO: Do not polute scene graph with .z
  282. if ( object.renderDepth !== null ) {
  283. object.z = object.renderDepth;
  284. } else {
  285. vector3.getPositionFromMatrix( object.matrixWorld );
  286. vector3.applyProjection( cameraViewProjectionMatrix );
  287. object.z = vector3.z;
  288. }
  289. if ( object.material.transparent === true ) {
  290. objectsTransparent.push( object );
  291. } else {
  292. objectsOpaque.push( object );
  293. }
  294. }
  295. for ( var i = 0, l = object.children.length; i < l; i ++ ) {
  296. projectObject( object.children[ i ], camera );
  297. }
  298. };
  299. var sortOpaque = function ( a, b ) {
  300. return a.z - b.z;
  301. };
  302. var sortTransparent = function ( a, b ) {
  303. return a.z !== b.z ? b.z - a.z : b.id - a.id;
  304. };
  305. var currentBuffer, currentMaterial, currentProgram;
  306. var locations = {};
  307. var renderObject = function ( object, camera ) {
  308. var buffer = getBuffer( object.geometry, object.material );
  309. var material = object.material;
  310. if ( material !== currentMaterial ) {
  311. var program = getProgram( object.material );
  312. if ( program !== currentProgram ) {
  313. gl.useProgram( program );
  314. locations.modelViewMatrix = gl.getUniformLocation( program, 'modelViewMatrix' );
  315. locations.normalMatrix = gl.getUniformLocation( program, 'normalMatrix' );
  316. locations.projectionMatrix = gl.getUniformLocation( program, 'projectionMatrix' );
  317. locations.position = gl.getAttribLocation( program, 'position' );
  318. locations.normal = gl.getAttribLocation( program, 'normal' );
  319. gl.uniformMatrix4fv( locations.projectionMatrix, false, camera.projectionMatrix.elements );
  320. currentProgram = program;
  321. }
  322. if ( material instanceof THREE.MeshNormalMaterial ) {
  323. gl.uniform1f( gl.getUniformLocation( program, 'opacity' ), material.opacity );
  324. } else if ( material instanceof THREE.ShaderMaterial ) {
  325. var uniforms = material.uniforms;
  326. for ( var uniform in uniforms ) {
  327. var location = gl.getUniformLocation( program, uniform );
  328. var type = uniforms[ uniform ].type;
  329. var value = uniforms[ uniform ].value;
  330. if ( type === "i" ) { // single integer
  331. gl.uniform1i( location, value );
  332. } else if ( type === "f" ) { // single float
  333. gl.uniform1f( location, value );
  334. } else if ( type === "v2" ) { // single THREE.Vector2
  335. gl.uniform2f( location, value.x, value.y );
  336. } else if ( type === "v3" ) { // single THREE.Vector3
  337. gl.uniform3f( location, value.x, value.y, value.z );
  338. } else if ( type === "v4" ) { // single THREE.Vector4
  339. gl.uniform4f( location, value.x, value.y, value.z, value.w );
  340. } else if ( type === "c" ) { // single THREE.Color
  341. gl.uniform3f( location, value.r, value.g, value.b );
  342. }
  343. }
  344. }
  345. currentMaterial = material;
  346. }
  347. if ( buffer !== currentBuffer ) {
  348. gl.bindBuffer( gl.ARRAY_BUFFER, buffer.positions );
  349. gl.enableVertexAttribArray( locations.position );
  350. gl.vertexAttribPointer( locations.position, 3, gl.FLOAT, false, 0, 0 );
  351. if ( locations.normal >= 0 ) {
  352. gl.bindBuffer( gl.ARRAY_BUFFER, buffer.normals );
  353. gl.enableVertexAttribArray( locations.normal );
  354. gl.vertexAttribPointer( locations.normal, 3, gl.FLOAT, false, 0, 0 );
  355. }
  356. currentBuffer = buffer;
  357. }
  358. modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld );
  359. normalMatrix.getNormalMatrix( modelViewMatrix );
  360. gl.uniformMatrix4fv( locations.modelViewMatrix, false, modelViewMatrix.elements );
  361. gl.uniformMatrix3fv( locations.normalMatrix, false, normalMatrix.elements );
  362. gl.drawArrays( gl.TRIANGLES, 0, buffer.count );
  363. scope.info.render.calls ++;
  364. };
  365. this.render = function ( scene, camera ) {
  366. if ( this.autoClear === true ) this.clear();
  367. scene.updateMatrixWorld();
  368. if ( camera.parent === undefined ) camera.updateMatrixWorld();
  369. camera.matrixWorldInverse.getInverse( camera.matrixWorld );
  370. cameraViewProjectionMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse );
  371. frustum.setFromMatrix( cameraViewProjectionMatrix );
  372. objectsOpaque.length = 0;
  373. objectsTransparent.length = 0;
  374. scope.info.render.calls = 0;
  375. currentBuffer = undefined;
  376. currentMaterial = undefined;
  377. currentProgram = undefined;
  378. projectObject( scene, camera );
  379. if ( objectsOpaque.length > 0 ) {
  380. objectsOpaque.sort( sortOpaque );
  381. setBlending( THREE.NoBlending );
  382. for ( var i = 0, l = objectsOpaque.length; i < l; i ++ ) {
  383. renderObject( objectsOpaque[ i ], camera );
  384. }
  385. }
  386. if ( objectsTransparent.length > 0 ) {
  387. objectsTransparent.sort( sortTransparent );
  388. setBlending( THREE.NormalBlending );
  389. for ( var i = 0, l = objectsTransparent.length; i < l; i ++ ) {
  390. renderObject( objectsTransparent[ i ], camera );
  391. }
  392. }
  393. };
  394. };