|
@@ -0,0 +1,310 @@
|
|
|
|
+/**
|
|
|
|
+ * @author mikael emtinger / http://gomo.se/
|
|
|
|
+ * @author alteredq / http://alteredqualia.com/
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+THREE.LensFlarePlugin = function ( ) {
|
|
|
|
+
|
|
|
|
+ var _gl, _renderer, _lensFlare = {};
|
|
|
|
+
|
|
|
|
+ this.init = function ( renderer ) {
|
|
|
|
+
|
|
|
|
+ _gl = renderer.context;
|
|
|
|
+ _renderer = renderer;
|
|
|
|
+
|
|
|
|
+ _lensFlare.vertices = new Float32Array( 8 + 8 );
|
|
|
|
+ _lensFlare.faces = new Uint16Array( 6 );
|
|
|
|
+
|
|
|
|
+ var i = 0;
|
|
|
|
+ _lensFlare.vertices[ i++ ] = -1; _lensFlare.vertices[ i++ ] = -1; // vertex
|
|
|
|
+ _lensFlare.vertices[ i++ ] = 0; _lensFlare.vertices[ i++ ] = 0; // uv... etc.
|
|
|
|
+
|
|
|
|
+ _lensFlare.vertices[ i++ ] = 1; _lensFlare.vertices[ i++ ] = -1;
|
|
|
|
+ _lensFlare.vertices[ i++ ] = 1; _lensFlare.vertices[ i++ ] = 0;
|
|
|
|
+
|
|
|
|
+ _lensFlare.vertices[ i++ ] = 1; _lensFlare.vertices[ i++ ] = 1;
|
|
|
|
+ _lensFlare.vertices[ i++ ] = 1; _lensFlare.vertices[ i++ ] = 1;
|
|
|
|
+
|
|
|
|
+ _lensFlare.vertices[ i++ ] = -1; _lensFlare.vertices[ i++ ] = 1;
|
|
|
|
+ _lensFlare.vertices[ i++ ] = 0; _lensFlare.vertices[ i++ ] = 1;
|
|
|
|
+
|
|
|
|
+ i = 0;
|
|
|
|
+ _lensFlare.faces[ i++ ] = 0; _lensFlare.faces[ i++ ] = 1; _lensFlare.faces[ i++ ] = 2;
|
|
|
|
+ _lensFlare.faces[ i++ ] = 0; _lensFlare.faces[ i++ ] = 2; _lensFlare.faces[ i++ ] = 3;
|
|
|
|
+
|
|
|
|
+ // buffers
|
|
|
|
+
|
|
|
|
+ _lensFlare.vertexBuffer = _gl.createBuffer();
|
|
|
|
+ _lensFlare.elementBuffer = _gl.createBuffer();
|
|
|
|
+
|
|
|
|
+ _gl.bindBuffer( _gl.ARRAY_BUFFER, _lensFlare.vertexBuffer );
|
|
|
|
+ _gl.bufferData( _gl.ARRAY_BUFFER, _lensFlare.vertices, _gl.STATIC_DRAW );
|
|
|
|
+
|
|
|
|
+ _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, _lensFlare.elementBuffer );
|
|
|
|
+ _gl.bufferData( _gl.ELEMENT_ARRAY_BUFFER, _lensFlare.faces, _gl.STATIC_DRAW );
|
|
|
|
+
|
|
|
|
+ // textures
|
|
|
|
+
|
|
|
|
+ _lensFlare.tempTexture = _gl.createTexture();
|
|
|
|
+ _lensFlare.occlusionTexture = _gl.createTexture();
|
|
|
|
+
|
|
|
|
+ _gl.bindTexture( _gl.TEXTURE_2D, _lensFlare.tempTexture );
|
|
|
|
+ _gl.texImage2D( _gl.TEXTURE_2D, 0, _gl.RGB, 16, 16, 0, _gl.RGB, _gl.UNSIGNED_BYTE, null );
|
|
|
|
+ _gl.texParameteri( _gl.TEXTURE_2D, _gl.TEXTURE_WRAP_S, _gl.CLAMP_TO_EDGE );
|
|
|
|
+ _gl.texParameteri( _gl.TEXTURE_2D, _gl.TEXTURE_WRAP_T, _gl.CLAMP_TO_EDGE );
|
|
|
|
+ _gl.texParameteri( _gl.TEXTURE_2D, _gl.TEXTURE_MAG_FILTER, _gl.NEAREST );
|
|
|
|
+ _gl.texParameteri( _gl.TEXTURE_2D, _gl.TEXTURE_MIN_FILTER, _gl.NEAREST );
|
|
|
|
+
|
|
|
|
+ _gl.bindTexture( _gl.TEXTURE_2D, _lensFlare.occlusionTexture );
|
|
|
|
+ _gl.texImage2D( _gl.TEXTURE_2D, 0, _gl.RGBA, 16, 16, 0, _gl.RGBA, _gl.UNSIGNED_BYTE, null );
|
|
|
|
+ _gl.texParameteri( _gl.TEXTURE_2D, _gl.TEXTURE_WRAP_S, _gl.CLAMP_TO_EDGE );
|
|
|
|
+ _gl.texParameteri( _gl.TEXTURE_2D, _gl.TEXTURE_WRAP_T, _gl.CLAMP_TO_EDGE );
|
|
|
|
+ _gl.texParameteri( _gl.TEXTURE_2D, _gl.TEXTURE_MAG_FILTER, _gl.NEAREST );
|
|
|
|
+ _gl.texParameteri( _gl.TEXTURE_2D, _gl.TEXTURE_MIN_FILTER, _gl.NEAREST );
|
|
|
|
+
|
|
|
|
+ if ( _gl.getParameter( _gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS ) <= 0 ) {
|
|
|
|
+
|
|
|
|
+ _lensFlare.hasVertexTexture = false;
|
|
|
|
+ _lensFlare.program = createProgram( THREE.ShaderFlares[ "lensFlare" ] );
|
|
|
|
+
|
|
|
|
+ } else {
|
|
|
|
+
|
|
|
|
+ _lensFlare.hasVertexTexture = true;
|
|
|
|
+ _lensFlare.program = createProgram( THREE.ShaderFlares[ "lensFlareVertexTexture" ] );
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ _lensFlare.attributes = {};
|
|
|
|
+ _lensFlare.uniforms = {};
|
|
|
|
+
|
|
|
|
+ _lensFlare.attributes.vertex = _gl.getAttribLocation ( _lensFlare.program, "position" );
|
|
|
|
+ _lensFlare.attributes.uv = _gl.getAttribLocation ( _lensFlare.program, "uv" );
|
|
|
|
+
|
|
|
|
+ _lensFlare.uniforms.renderType = _gl.getUniformLocation( _lensFlare.program, "renderType" );
|
|
|
|
+ _lensFlare.uniforms.map = _gl.getUniformLocation( _lensFlare.program, "map" );
|
|
|
|
+ _lensFlare.uniforms.occlusionMap = _gl.getUniformLocation( _lensFlare.program, "occlusionMap" );
|
|
|
|
+ _lensFlare.uniforms.opacity = _gl.getUniformLocation( _lensFlare.program, "opacity" );
|
|
|
|
+ _lensFlare.uniforms.color = _gl.getUniformLocation( _lensFlare.program, "color" );
|
|
|
|
+ _lensFlare.uniforms.scale = _gl.getUniformLocation( _lensFlare.program, "scale" );
|
|
|
|
+ _lensFlare.uniforms.rotation = _gl.getUniformLocation( _lensFlare.program, "rotation" );
|
|
|
|
+ _lensFlare.uniforms.screenPosition = _gl.getUniformLocation( _lensFlare.program, "screenPosition" );
|
|
|
|
+
|
|
|
|
+ _lensFlare.attributesEnabled = false;
|
|
|
|
+
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * Render lens flares
|
|
|
|
+ * Method: renders 16x16 0xff00ff-colored points scattered over the light source area,
|
|
|
|
+ * reads these back and calculates occlusion.
|
|
|
|
+ * Then _lensFlare.update_lensFlares() is called to re-position and
|
|
|
|
+ * update transparency of flares. Then they are rendered.
|
|
|
|
+ *
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+ this.render = function ( scene, camera, viewportWidth, viewportHeight ) {
|
|
|
|
+
|
|
|
|
+ var _flaresList = scene.__webglFlares;
|
|
|
|
+
|
|
|
|
+ var object;
|
|
|
|
+
|
|
|
|
+ var o, ol = _flaresList.length;
|
|
|
|
+ var f, fl, flare;
|
|
|
|
+
|
|
|
|
+ var tempPosition = new THREE.Vector3();
|
|
|
|
+
|
|
|
|
+ var invAspect = viewportHeight / viewportWidth;
|
|
|
|
+ var halfViewportWidth = viewportWidth * 0.5;
|
|
|
|
+ var halfViewportHeight = viewportHeight * 0.5;
|
|
|
|
+
|
|
|
|
+ var size = 16 / viewportHeight;
|
|
|
|
+
|
|
|
|
+ var scale = new THREE.Vector2( size * invAspect, size );
|
|
|
|
+
|
|
|
|
+ var screenPosition = new THREE.Vector3( 1, 1, 0 );
|
|
|
|
+ var screenPositionPixels = new THREE.Vector2( 1, 1 );
|
|
|
|
+
|
|
|
|
+ var uniforms = _lensFlare.uniforms;
|
|
|
|
+ var attributes = _lensFlare.attributes;
|
|
|
|
+
|
|
|
|
+ // set _lensFlare program and reset blending
|
|
|
|
+
|
|
|
|
+ _gl.useProgram( _lensFlare.program );
|
|
|
|
+
|
|
|
|
+ if ( ! _lensFlare.attributesEnabled ) {
|
|
|
|
+
|
|
|
|
+ _gl.enableVertexAttribArray( _lensFlare.attributes.vertex );
|
|
|
|
+ _gl.enableVertexAttribArray( _lensFlare.attributes.uv );
|
|
|
|
+
|
|
|
|
+ _lensFlare.attributesEnabled = true;
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // loop through all lens flares to update their occlusion and positions
|
|
|
|
+ // setup gl and common used attribs/unforms
|
|
|
|
+
|
|
|
|
+ _gl.uniform1i( uniforms.occlusionMap, 0 );
|
|
|
|
+ _gl.uniform1i( uniforms.map, 1 );
|
|
|
|
+
|
|
|
|
+ _gl.bindBuffer( _gl.ARRAY_BUFFER, _lensFlare.vertexBuffer );
|
|
|
|
+ _gl.vertexAttribPointer( attributes.vertex, 2, _gl.FLOAT, false, 2 * 8, 0 );
|
|
|
|
+ _gl.vertexAttribPointer( attributes.uv, 2, _gl.FLOAT, false, 2 * 8, 8 );
|
|
|
|
+
|
|
|
|
+ _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, _lensFlare.elementBuffer );
|
|
|
|
+
|
|
|
|
+ _gl.disable( _gl.CULL_FACE );
|
|
|
|
+ _gl.depthMask( false );
|
|
|
|
+
|
|
|
|
+ for ( o = 0; o < ol; o ++ ) {
|
|
|
|
+
|
|
|
|
+ size = 16 / viewportHeight;
|
|
|
|
+ scale.set( size * invAspect, size );
|
|
|
|
+
|
|
|
|
+ // calc object screen position
|
|
|
|
+
|
|
|
|
+ object = _flaresList[ o ];
|
|
|
|
+
|
|
|
|
+ tempPosition.set( object.matrixWorld.n14, object.matrixWorld.n24, object.matrixWorld.n34 );
|
|
|
|
+
|
|
|
|
+ camera.matrixWorldInverse.multiplyVector3( tempPosition );
|
|
|
|
+ camera.projectionMatrix.multiplyVector3( tempPosition );
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ // setup arrays for gl programs
|
|
|
|
+
|
|
|
|
+ screenPosition.copy( tempPosition )
|
|
|
|
+
|
|
|
|
+ screenPositionPixels.x = screenPosition.x * halfViewportWidth + halfViewportWidth;
|
|
|
|
+ screenPositionPixels.y = screenPosition.y * halfViewportHeight + halfViewportHeight;
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ // screen cull
|
|
|
|
+
|
|
|
|
+ if ( _lensFlare.hasVertexTexture || (
|
|
|
|
+ screenPositionPixels.x > 0 &&
|
|
|
|
+ screenPositionPixels.x < viewportWidth &&
|
|
|
|
+ screenPositionPixels.y > 0 &&
|
|
|
|
+ screenPositionPixels.y < viewportHeight ) ) {
|
|
|
|
+
|
|
|
|
+ // save current RGB to temp texture
|
|
|
|
+
|
|
|
|
+ _gl.activeTexture( _gl.TEXTURE1 );
|
|
|
|
+ _gl.bindTexture( _gl.TEXTURE_2D, _lensFlare.tempTexture );
|
|
|
|
+ _gl.copyTexImage2D( _gl.TEXTURE_2D, 0, _gl.RGB, screenPositionPixels.x - 8, screenPositionPixels.y - 8, 16, 16, 0 );
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ // render pink quad
|
|
|
|
+
|
|
|
|
+ _gl.uniform1i( uniforms.renderType, 0 );
|
|
|
|
+ _gl.uniform2f( uniforms.scale, scale.x, scale.y );
|
|
|
|
+ _gl.uniform3f( uniforms.screenPosition, screenPosition.x, screenPosition.y, screenPosition.z );
|
|
|
|
+
|
|
|
|
+ _gl.disable( _gl.BLEND );
|
|
|
|
+ _gl.enable( _gl.DEPTH_TEST );
|
|
|
|
+
|
|
|
|
+ _gl.drawElements( _gl.TRIANGLES, 6, _gl.UNSIGNED_SHORT, 0 );
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ // copy result to occlusionMap
|
|
|
|
+
|
|
|
|
+ _gl.activeTexture( _gl.TEXTURE0 );
|
|
|
|
+ _gl.bindTexture( _gl.TEXTURE_2D, _lensFlare.occlusionTexture );
|
|
|
|
+ _gl.copyTexImage2D( _gl.TEXTURE_2D, 0, _gl.RGBA, screenPositionPixels.x - 8, screenPositionPixels.y - 8, 16, 16, 0 );
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ // restore graphics
|
|
|
|
+
|
|
|
|
+ _gl.uniform1i( uniforms.renderType, 1 );
|
|
|
|
+ _gl.disable( _gl.DEPTH_TEST );
|
|
|
|
+
|
|
|
|
+ _gl.activeTexture( _gl.TEXTURE1 );
|
|
|
|
+ _gl.bindTexture( _gl.TEXTURE_2D, _lensFlare.tempTexture );
|
|
|
|
+ _gl.drawElements( _gl.TRIANGLES, 6, _gl.UNSIGNED_SHORT, 0 );
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ // update object positions
|
|
|
|
+
|
|
|
|
+ object.positionScreen.copy( screenPosition )
|
|
|
|
+
|
|
|
|
+ if ( object.customUpdateCallback ) {
|
|
|
|
+
|
|
|
|
+ object.customUpdateCallback( object );
|
|
|
|
+
|
|
|
|
+ } else {
|
|
|
|
+
|
|
|
|
+ object.updateLensFlares();
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // render flares
|
|
|
|
+
|
|
|
|
+ _gl.uniform1i( uniforms.renderType, 2 );
|
|
|
|
+ _gl.enable( _gl.BLEND );
|
|
|
|
+
|
|
|
|
+ for ( f = 0, fl = object.lensFlares.length; f < fl; f ++ ) {
|
|
|
|
+
|
|
|
|
+ flare = object.lensFlares[ f ];
|
|
|
|
+
|
|
|
|
+ if ( flare.opacity > 0.001 && flare.scale > 0.001 ) {
|
|
|
|
+
|
|
|
|
+ screenPosition.x = flare.x;
|
|
|
|
+ screenPosition.y = flare.y;
|
|
|
|
+ screenPosition.z = flare.z;
|
|
|
|
+
|
|
|
|
+ size = flare.size * flare.scale / viewportHeight;
|
|
|
|
+
|
|
|
|
+ scale.x = size * invAspect;
|
|
|
|
+ scale.y = size;
|
|
|
|
+
|
|
|
|
+ _gl.uniform3f( uniforms.screenPosition, screenPosition.x, screenPosition.y, screenPosition.z );
|
|
|
|
+ _gl.uniform2f( uniforms.scale, scale.x, scale.y );
|
|
|
|
+ _gl.uniform1f( uniforms.rotation, flare.rotation );
|
|
|
|
+
|
|
|
|
+ _gl.uniform1f( uniforms.opacity, flare.opacity );
|
|
|
|
+ _gl.uniform3f( uniforms.color, flare.color.r, flare.color.g, flare.color.b );
|
|
|
|
+
|
|
|
|
+ _renderer.setBlending( flare.blending );
|
|
|
|
+ _renderer.setTexture( flare.texture, 1 );
|
|
|
|
+
|
|
|
|
+ _gl.drawElements( _gl.TRIANGLES, 6, _gl.UNSIGNED_SHORT, 0 );
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // restore gl
|
|
|
|
+
|
|
|
|
+ _gl.enable( _gl.CULL_FACE );
|
|
|
|
+ _gl.enable( _gl.DEPTH_TEST );
|
|
|
|
+ _gl.depthMask( true );
|
|
|
|
+
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ function createProgram ( shader ) {
|
|
|
|
+
|
|
|
|
+ var program = _gl.createProgram();
|
|
|
|
+
|
|
|
|
+ var fragmentShader = _gl.createShader( _gl.FRAGMENT_SHADER );
|
|
|
|
+ var vertexShader = _gl.createShader( _gl.VERTEX_SHADER );
|
|
|
|
+
|
|
|
|
+ _gl.shaderSource( fragmentShader, shader.fragmentShader );
|
|
|
|
+ _gl.shaderSource( vertexShader, shader.vertexShader );
|
|
|
|
+
|
|
|
|
+ _gl.compileShader( fragmentShader );
|
|
|
|
+ _gl.compileShader( vertexShader );
|
|
|
|
+
|
|
|
|
+ _gl.attachShader( program, fragmentShader );
|
|
|
|
+ _gl.attachShader( program, vertexShader );
|
|
|
|
+
|
|
|
|
+ _gl.linkProgram( program );
|
|
|
|
+
|
|
|
|
+ return program;
|
|
|
|
+
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+};
|