LensFlarePlugin.js 12 KB

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