LensFlarePlugin.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474
  1. /**
  2. * @author mikael emtinger / http://gomo.se/
  3. * @author alteredq / http://alteredqualia.com/
  4. */
  5. THREE.LensFlarePlugin = function ( renderer, flares ) {
  6. var gl = renderer.context;
  7. var state = renderer.state;
  8. var vertexBuffer, elementBuffer;
  9. var program, attributes, uniforms;
  10. var hasVertexTexture;
  11. var tempTexture, occlusionTexture;
  12. var init = function () {
  13. var vertices = new Float32Array( [
  14. -1, -1, 0, 0,
  15. 1, -1, 1, 0,
  16. 1, 1, 1, 1,
  17. -1, 1, 0, 1
  18. ] );
  19. var faces = new Uint16Array( [
  20. 0, 1, 2,
  21. 0, 2, 3
  22. ] );
  23. // buffers
  24. vertexBuffer = gl.createBuffer();
  25. elementBuffer = gl.createBuffer();
  26. gl.bindBuffer( gl.ARRAY_BUFFER, vertexBuffer );
  27. gl.bufferData( gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW );
  28. gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, elementBuffer );
  29. gl.bufferData( gl.ELEMENT_ARRAY_BUFFER, faces, gl.STATIC_DRAW );
  30. // textures
  31. tempTexture = gl.createTexture();
  32. occlusionTexture = gl.createTexture();
  33. state.bindTexture( gl.TEXTURE_2D, tempTexture );
  34. gl.texImage2D( gl.TEXTURE_2D, 0, gl.RGB, 16, 16, 0, gl.RGB, gl.UNSIGNED_BYTE, null );
  35. gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE );
  36. gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE );
  37. gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST );
  38. gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST );
  39. state.bindTexture( gl.TEXTURE_2D, occlusionTexture );
  40. gl.texImage2D( gl.TEXTURE_2D, 0, gl.RGBA, 16, 16, 0, gl.RGBA, gl.UNSIGNED_BYTE, null );
  41. gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE );
  42. gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE );
  43. gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST );
  44. gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST );
  45. hasVertexTexture = gl.getParameter( gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS ) > 0;
  46. var shader;
  47. if ( hasVertexTexture ) {
  48. shader = {
  49. vertexShader: [
  50. "uniform lowp int renderType;",
  51. "uniform vec3 screenPosition;",
  52. "uniform vec2 scale;",
  53. "uniform float rotation;",
  54. "uniform sampler2D occlusionMap;",
  55. "attribute vec2 position;",
  56. "attribute vec2 uv;",
  57. "varying vec2 vUV;",
  58. "varying float vVisibility;",
  59. "void main() {",
  60. "vUV = uv;",
  61. "vec2 pos = position;",
  62. "if( renderType == 2 ) {",
  63. "vec4 visibility = texture2D( occlusionMap, vec2( 0.1, 0.1 ) );",
  64. "visibility += texture2D( occlusionMap, vec2( 0.5, 0.1 ) );",
  65. "visibility += texture2D( occlusionMap, vec2( 0.9, 0.1 ) );",
  66. "visibility += texture2D( occlusionMap, vec2( 0.9, 0.5 ) );",
  67. "visibility += texture2D( occlusionMap, vec2( 0.9, 0.9 ) );",
  68. "visibility += texture2D( occlusionMap, vec2( 0.5, 0.9 ) );",
  69. "visibility += texture2D( occlusionMap, vec2( 0.1, 0.9 ) );",
  70. "visibility += texture2D( occlusionMap, vec2( 0.1, 0.5 ) );",
  71. "visibility += texture2D( occlusionMap, vec2( 0.5, 0.5 ) );",
  72. "vVisibility = visibility.r / 9.0;",
  73. "vVisibility *= 1.0 - visibility.g / 9.0;",
  74. "vVisibility *= visibility.b / 9.0;",
  75. "vVisibility *= 1.0 - visibility.a / 9.0;",
  76. "pos.x = cos( rotation ) * position.x - sin( rotation ) * position.y;",
  77. "pos.y = sin( rotation ) * position.x + cos( rotation ) * position.y;",
  78. "}",
  79. "gl_Position = vec4( ( pos * scale + screenPosition.xy ).xy, screenPosition.z, 1.0 );",
  80. "}"
  81. ].join( "\n" ),
  82. fragmentShader: [
  83. "uniform lowp int renderType;",
  84. "uniform sampler2D map;",
  85. "uniform float opacity;",
  86. "uniform vec3 color;",
  87. "varying vec2 vUV;",
  88. "varying float vVisibility;",
  89. "void main() {",
  90. // pink square
  91. "if( renderType == 0 ) {",
  92. "gl_FragColor = vec4( 1.0, 0.0, 1.0, 0.0 );",
  93. // restore
  94. "} else if( renderType == 1 ) {",
  95. "gl_FragColor = texture2D( map, vUV );",
  96. // flare
  97. "} else {",
  98. "vec4 texture = texture2D( map, vUV );",
  99. "texture.a *= opacity * vVisibility;",
  100. "gl_FragColor = texture;",
  101. "gl_FragColor.rgb *= color;",
  102. "}",
  103. "}"
  104. ].join( "\n" )
  105. };
  106. } else {
  107. shader = {
  108. vertexShader: [
  109. "uniform lowp int renderType;",
  110. "uniform vec3 screenPosition;",
  111. "uniform vec2 scale;",
  112. "uniform float rotation;",
  113. "attribute vec2 position;",
  114. "attribute vec2 uv;",
  115. "varying vec2 vUV;",
  116. "void main() {",
  117. "vUV = uv;",
  118. "vec2 pos = position;",
  119. "if( renderType == 2 ) {",
  120. "pos.x = cos( rotation ) * position.x - sin( rotation ) * position.y;",
  121. "pos.y = sin( rotation ) * position.x + cos( rotation ) * position.y;",
  122. "}",
  123. "gl_Position = vec4( ( pos * scale + screenPosition.xy ).xy, screenPosition.z, 1.0 );",
  124. "}"
  125. ].join( "\n" ),
  126. fragmentShader: [
  127. "precision mediump float;",
  128. "uniform lowp int renderType;",
  129. "uniform sampler2D map;",
  130. "uniform sampler2D occlusionMap;",
  131. "uniform float opacity;",
  132. "uniform vec3 color;",
  133. "varying vec2 vUV;",
  134. "void main() {",
  135. // pink square
  136. "if( renderType == 0 ) {",
  137. "gl_FragColor = vec4( texture2D( map, vUV ).rgb, 0.0 );",
  138. // restore
  139. "} else if( renderType == 1 ) {",
  140. "gl_FragColor = texture2D( map, vUV );",
  141. // flare
  142. "} else {",
  143. "float visibility = texture2D( occlusionMap, vec2( 0.5, 0.1 ) ).a;",
  144. "visibility += texture2D( occlusionMap, vec2( 0.9, 0.5 ) ).a;",
  145. "visibility += texture2D( occlusionMap, vec2( 0.5, 0.9 ) ).a;",
  146. "visibility += texture2D( occlusionMap, vec2( 0.1, 0.5 ) ).a;",
  147. "visibility = ( 1.0 - visibility / 4.0 );",
  148. "vec4 texture = texture2D( map, vUV );",
  149. "texture.a *= opacity * visibility;",
  150. "gl_FragColor = texture;",
  151. "gl_FragColor.rgb *= color;",
  152. "}",
  153. "}"
  154. ].join( "\n" )
  155. };
  156. }
  157. program = createProgram( shader );
  158. attributes = {
  159. vertex: gl.getAttribLocation ( program, "position" ),
  160. uv: gl.getAttribLocation ( program, "uv" )
  161. };
  162. uniforms = {
  163. renderType: gl.getUniformLocation( program, "renderType" ),
  164. map: gl.getUniformLocation( program, "map" ),
  165. occlusionMap: gl.getUniformLocation( program, "occlusionMap" ),
  166. opacity: gl.getUniformLocation( program, "opacity" ),
  167. color: gl.getUniformLocation( program, "color" ),
  168. scale: gl.getUniformLocation( program, "scale" ),
  169. rotation: gl.getUniformLocation( program, "rotation" ),
  170. screenPosition: gl.getUniformLocation( program, "screenPosition" )
  171. };
  172. };
  173. /*
  174. * Render lens flares
  175. * Method: renders 16x16 0xff00ff-colored points scattered over the light source area,
  176. * reads these back and calculates occlusion.
  177. */
  178. this.render = function ( scene, camera, viewportWidth, viewportHeight ) {
  179. if ( flares.length === 0 ) return;
  180. var tempPosition = new THREE.Vector3();
  181. var invAspect = viewportHeight / viewportWidth,
  182. halfViewportWidth = viewportWidth * 0.5,
  183. halfViewportHeight = viewportHeight * 0.5;
  184. var size = 16 / viewportHeight,
  185. scale = new THREE.Vector2( size * invAspect, size );
  186. var screenPosition = new THREE.Vector3( 1, 1, 0 ),
  187. screenPositionPixels = new THREE.Vector2( 1, 1 );
  188. if ( program === undefined ) {
  189. init();
  190. }
  191. gl.useProgram( program );
  192. state.initAttributes();
  193. state.enableAttribute( attributes.vertex );
  194. state.enableAttribute( attributes.uv );
  195. state.disableUnusedAttributes();
  196. // loop through all lens flares to update their occlusion and positions
  197. // setup gl and common used attribs/unforms
  198. gl.uniform1i( uniforms.occlusionMap, 0 );
  199. gl.uniform1i( uniforms.map, 1 );
  200. gl.bindBuffer( gl.ARRAY_BUFFER, vertexBuffer );
  201. gl.vertexAttribPointer( attributes.vertex, 2, gl.FLOAT, false, 2 * 8, 0 );
  202. gl.vertexAttribPointer( attributes.uv, 2, gl.FLOAT, false, 2 * 8, 8 );
  203. gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, elementBuffer );
  204. gl.disable( gl.CULL_FACE );
  205. gl.depthMask( false );
  206. for ( var i = 0, l = flares.length; i < l; i ++ ) {
  207. size = 16 / viewportHeight;
  208. scale.set( size * invAspect, size );
  209. // calc object screen position
  210. var flare = flares[ i ];
  211. tempPosition.set( flare.matrixWorld.elements[12], flare.matrixWorld.elements[13], flare.matrixWorld.elements[14] );
  212. tempPosition.applyMatrix4( camera.matrixWorldInverse );
  213. tempPosition.applyProjection( camera.projectionMatrix );
  214. // setup arrays for gl programs
  215. screenPosition.copy( tempPosition );
  216. screenPositionPixels.x = screenPosition.x * halfViewportWidth + halfViewportWidth;
  217. screenPositionPixels.y = screenPosition.y * halfViewportHeight + halfViewportHeight;
  218. // screen cull
  219. if ( hasVertexTexture || (
  220. screenPositionPixels.x > 0 &&
  221. screenPositionPixels.x < viewportWidth &&
  222. screenPositionPixels.y > 0 &&
  223. screenPositionPixels.y < viewportHeight ) ) {
  224. // save current RGB to temp texture
  225. state.activeTexture( gl.TEXTURE0 );
  226. state.bindTexture( gl.TEXTURE_2D, null );
  227. state.activeTexture( gl.TEXTURE1 );
  228. state.bindTexture( gl.TEXTURE_2D, tempTexture );
  229. gl.copyTexImage2D( gl.TEXTURE_2D, 0, gl.RGB, screenPositionPixels.x - 8, screenPositionPixels.y - 8, 16, 16, 0 );
  230. // render pink quad
  231. gl.uniform1i( uniforms.renderType, 0 );
  232. gl.uniform2f( uniforms.scale, scale.x, scale.y );
  233. gl.uniform3f( uniforms.screenPosition, screenPosition.x, screenPosition.y, screenPosition.z );
  234. state.setBlend( false );
  235. gl.enable( gl.DEPTH_TEST );
  236. gl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 );
  237. // copy result to occlusionMap
  238. state.activeTexture( gl.TEXTURE0 );
  239. state.bindTexture( gl.TEXTURE_2D, occlusionTexture );
  240. gl.copyTexImage2D( gl.TEXTURE_2D, 0, gl.RGBA, screenPositionPixels.x - 8, screenPositionPixels.y - 8, 16, 16, 0 );
  241. // restore graphics
  242. gl.uniform1i( uniforms.renderType, 1 );
  243. gl.disable( gl.DEPTH_TEST );
  244. state.activeTexture( gl.TEXTURE1 );
  245. state.bindTexture( gl.TEXTURE_2D, tempTexture );
  246. gl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 );
  247. // update object positions
  248. flare.positionScreen.copy( screenPosition );
  249. if ( flare.customUpdateCallback ) {
  250. flare.customUpdateCallback( flare );
  251. } else {
  252. flare.updateLensFlares();
  253. }
  254. // render flares
  255. gl.uniform1i( uniforms.renderType, 2 );
  256. state.setBlend( true );
  257. for ( var j = 0, jl = flare.lensFlares.length; j < jl; j ++ ) {
  258. var sprite = flare.lensFlares[ j ];
  259. if ( sprite.opacity > 0.001 && sprite.scale > 0.001 ) {
  260. screenPosition.x = sprite.x;
  261. screenPosition.y = sprite.y;
  262. screenPosition.z = sprite.z;
  263. size = sprite.size * sprite.scale / viewportHeight;
  264. scale.x = size * invAspect;
  265. scale.y = size;
  266. gl.uniform3f( uniforms.screenPosition, screenPosition.x, screenPosition.y, screenPosition.z );
  267. gl.uniform2f( uniforms.scale, scale.x, scale.y );
  268. gl.uniform1f( uniforms.rotation, sprite.rotation );
  269. gl.uniform1f( uniforms.opacity, sprite.opacity );
  270. gl.uniform3f( uniforms.color, sprite.color.r, sprite.color.g, sprite.color.b );
  271. state.setBlending( sprite.blending, sprite.blendEquation, sprite.blendSrc, sprite.blendDst );
  272. renderer.setTexture( sprite.texture, 1 );
  273. gl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 );
  274. }
  275. }
  276. }
  277. }
  278. // restore gl
  279. gl.enable( gl.CULL_FACE );
  280. gl.enable( gl.DEPTH_TEST );
  281. gl.depthMask( true );
  282. renderer.resetGLState();
  283. };
  284. function createProgram ( shader ) {
  285. var program = gl.createProgram();
  286. var fragmentShader = gl.createShader( gl.FRAGMENT_SHADER );
  287. var vertexShader = gl.createShader( gl.VERTEX_SHADER );
  288. var prefix = "precision " + renderer.getPrecision() + " float;\n";
  289. gl.shaderSource( fragmentShader, prefix + shader.fragmentShader );
  290. gl.shaderSource( vertexShader, prefix + shader.vertexShader );
  291. gl.compileShader( fragmentShader );
  292. gl.compileShader( vertexShader );
  293. gl.attachShader( program, fragmentShader );
  294. gl.attachShader( program, vertexShader );
  295. gl.linkProgram( program );
  296. return program;
  297. }
  298. };