/** * @author mikael emtinger / http://gomo.se/ * @author alteredq / http://alteredqualia.com/ */ THREE.SpritePlugin = function () { var _gl, _renderer, _texture; var vertices, faces, vertexBuffer, elementBuffer; var program, attributes, uniforms; this.init = function ( renderer ) { _gl = renderer.context; _renderer = renderer; vertices = new Float32Array( [ - 0.5, - 0.5, 0, 0, 0.5, - 0.5, 1, 0, 0.5, 0.5, 1, 1, - 0.5, 0.5, 0, 1 ] ); faces = new Uint16Array( [ 0, 1, 2, 0, 2, 3 ] ); vertexBuffer = _gl.createBuffer(); elementBuffer = _gl.createBuffer(); _gl.bindBuffer( _gl.ARRAY_BUFFER, vertexBuffer ); _gl.bufferData( _gl.ARRAY_BUFFER, vertices, _gl.STATIC_DRAW ); _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, elementBuffer ); _gl.bufferData( _gl.ELEMENT_ARRAY_BUFFER, faces, _gl.STATIC_DRAW ); program = createProgram(); attributes = { position: _gl.getAttribLocation ( program, 'position' ), uv: _gl.getAttribLocation ( program, 'uv' ) }; uniforms = { uvOffset: _gl.getUniformLocation( program, 'uvOffset' ), uvScale: _gl.getUniformLocation( program, 'uvScale' ), rotation: _gl.getUniformLocation( program, 'rotation' ), scale: _gl.getUniformLocation( program, 'scale' ), color: _gl.getUniformLocation( program, 'color' ), map: _gl.getUniformLocation( program, 'map' ), opacity: _gl.getUniformLocation( program, 'opacity' ), modelViewMatrix: _gl.getUniformLocation( program, 'modelViewMatrix' ), projectionMatrix: _gl.getUniformLocation( program, 'projectionMatrix' ), fogType: _gl.getUniformLocation( program, 'fogType' ), fogDensity: _gl.getUniformLocation( program, 'fogDensity' ), fogNear: _gl.getUniformLocation( program, 'fogNear' ), fogFar: _gl.getUniformLocation( program, 'fogFar' ), fogColor: _gl.getUniformLocation( program, 'fogColor' ), alphaTest: _gl.getUniformLocation( program, 'alphaTest' ) }; var canvas = document.createElement( 'canvas' ); canvas.width = 8; canvas.height = 8; var context = canvas.getContext( '2d' ); context.fillStyle = '#ffffff'; context.fillRect( 0, 0, canvas.width, canvas.height ); _texture = new THREE.Texture( canvas ); _texture.needsUpdate = true; }; this.render = function ( scene, camera, viewportWidth, viewportHeight ) { var sprites = scene.__webglSprites, nSprites = sprites.length; if ( ! nSprites ) return; // setup gl _gl.useProgram( program ); _gl.enableVertexAttribArray( attributes.position ); _gl.enableVertexAttribArray( attributes.uv ); _gl.disable( _gl.CULL_FACE ); _gl.enable( _gl.BLEND ); _gl.bindBuffer( _gl.ARRAY_BUFFER, vertexBuffer ); _gl.vertexAttribPointer( attributes.position, 2, _gl.FLOAT, false, 2 * 8, 0 ); _gl.vertexAttribPointer( attributes.uv, 2, _gl.FLOAT, false, 2 * 8, 8 ); _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, elementBuffer ); _gl.uniformMatrix4fv( uniforms.projectionMatrix, false, camera.projectionMatrix.elements ); _gl.activeTexture( _gl.TEXTURE0 ); _gl.uniform1i( uniforms.map, 0 ); var oldFogType = 0; var sceneFogType = 0; var fog = scene.fog; if ( fog ) { _gl.uniform3f( uniforms.fogColor, fog.color.r, fog.color.g, fog.color.b ); if ( fog instanceof THREE.Fog ) { _gl.uniform1f( uniforms.fogNear, fog.near ); _gl.uniform1f( uniforms.fogFar, fog.far ); _gl.uniform1i( uniforms.fogType, 1 ); oldFogType = 1; sceneFogType = 1; } else if ( fog instanceof THREE.FogExp2 ) { _gl.uniform1f( uniforms.fogDensity, fog.density ); _gl.uniform1i( uniforms.fogType, 2 ); oldFogType = 2; sceneFogType = 2; } } else { _gl.uniform1i( uniforms.fogType, 0 ); oldFogType = 0; sceneFogType = 0; } // update positions and sort var i, sprite, material, fogType, scale = []; for( i = 0; i < nSprites; i ++ ) { sprite = sprites[ i ]; material = sprite.material; if ( sprite.visible === false ) continue; sprite._modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, sprite.matrixWorld ); sprite.z = - sprite._modelViewMatrix.elements[ 14 ]; } sprites.sort( painterSortStable ); // render all sprites for( i = 0; i < nSprites; i ++ ) { sprite = sprites[ i ]; if ( sprite.visible === false ) continue; material = sprite.material; _gl.uniform1f( uniforms.alphaTest, material.alphaTest ); _gl.uniformMatrix4fv( uniforms.modelViewMatrix, false, sprite._modelViewMatrix.elements ); scale[ 0 ] = sprite.scale.x; scale[ 1 ] = sprite.scale.y; if ( scene.fog && material.fog ) { fogType = sceneFogType; } else { fogType = 0; } if ( oldFogType !== fogType ) { _gl.uniform1i( uniforms.fogType, fogType ); oldFogType = fogType; } if ( material.map !== null ) { _gl.uniform2f( uniforms.uvOffset, material.map.offset.x, material.map.offset.y ); _gl.uniform2f( uniforms.uvScale, material.map.repeat.x, material.map.repeat.y ); } else { _gl.uniform2f( uniforms.uvOffset, 0, 0 ); _gl.uniform2f( uniforms.uvScale, 1, 1 ); } _gl.uniform1f( uniforms.opacity, material.opacity ); _gl.uniform3f( uniforms.color, material.color.r, material.color.g, material.color.b ); _gl.uniform1f( uniforms.rotation, material.rotation ); _gl.uniform2fv( uniforms.scale, scale ); _renderer.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst ); _renderer.setDepthTest( material.depthTest ); _renderer.setDepthWrite( material.depthWrite ); if ( material.map && material.map.image && material.map.image.width ) { _renderer.setTexture( material.map, 0 ); } else { _renderer.setTexture( _texture, 0 ); } _gl.drawElements( _gl.TRIANGLES, 6, _gl.UNSIGNED_SHORT, 0 ); } // restore gl _gl.enable( _gl.CULL_FACE ); }; function createProgram () { var program = _gl.createProgram(); var vertexShader = _gl.createShader( _gl.VERTEX_SHADER ); var fragmentShader = _gl.createShader( _gl.FRAGMENT_SHADER ); _gl.shaderSource( vertexShader, [ 'precision ' + _renderer.getPrecision() + ' float;', 'uniform mat4 modelViewMatrix;', 'uniform mat4 projectionMatrix;', 'uniform float rotation;', 'uniform vec2 scale;', 'uniform vec2 uvOffset;', 'uniform vec2 uvScale;', 'attribute vec2 position;', 'attribute vec2 uv;', 'varying vec2 vUV;', 'void main() {', 'vUV = uvOffset + uv * uvScale;', 'vec2 alignedPosition = position * scale;', 'vec2 rotatedPosition;', 'rotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y;', 'rotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y;', 'vec4 finalPosition;', 'finalPosition = modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 );', 'finalPosition.xy += rotatedPosition;', 'finalPosition = projectionMatrix * finalPosition;', 'gl_Position = finalPosition;', '}' ].join( '\n' ) ); _gl.shaderSource( fragmentShader, [ 'precision ' + _renderer.getPrecision() + ' float;', 'uniform vec3 color;', 'uniform sampler2D map;', 'uniform float opacity;', 'uniform int fogType;', 'uniform vec3 fogColor;', 'uniform float fogDensity;', 'uniform float fogNear;', 'uniform float fogFar;', 'uniform float alphaTest;', 'varying vec2 vUV;', 'void main() {', 'vec4 texture = texture2D( map, vUV );', 'if ( texture.a < alphaTest ) discard;', 'gl_FragColor = vec4( color * texture.xyz, texture.a * opacity );', 'if ( fogType > 0 ) {', 'float depth = gl_FragCoord.z / gl_FragCoord.w;', 'float fogFactor = 0.0;', 'if ( fogType == 1 ) {', 'fogFactor = smoothstep( fogNear, fogFar, depth );', '} else {', 'const float LOG2 = 1.442695;', 'float fogFactor = exp2( - fogDensity * fogDensity * depth * depth * LOG2 );', 'fogFactor = 1.0 - clamp( fogFactor, 0.0, 1.0 );', '}', 'gl_FragColor = mix( gl_FragColor, vec4( fogColor, gl_FragColor.w ), fogFactor );', '}', '}' ].join( '\n' ) ); _gl.compileShader( vertexShader ); _gl.compileShader( fragmentShader ); _gl.attachShader( program, vertexShader ); _gl.attachShader( program, fragmentShader ); _gl.linkProgram( program ); return program; }; function painterSortStable ( a, b ) { if ( a.z !== b.z ) { return b.z - a.z; } else { return b.id - a.id; } }; };