提交 c3ab2eb1 编写于 作者: M Mikael Emtinger

Rewrote lens flares to not use readPixels. There are different solutions based...

Rewrote lens flares to not use readPixels. There are different solutions based on availablity of vertex textures. If available, occlusion calculation is more exact and handles partial occlusion on screen edges. If not available, occlusion calculation is less exact (because it's made on each fragment so it has to be fast) and partial occlusion by screen edge is not possible (same reason).

It's not possible for the lens flare to update on visibility any more, only its screen position. Potentially, we could add offset+factor for opacity/scale/rotation that is dependent on visibility in shader. Or just custom shaders.

Anyway, it's way faster than readPixels :)
上级 4470e0f6
......@@ -51,7 +51,7 @@ THREE.LensFlare.prototype.add = function( texture, size, distance, blending ) {
* Set myLensFlare.customUpdateCallback to alter the flares in your project specific way.
*/
THREE.LensFlare.prototype.updateLensFlares = function( visibility ) {
THREE.LensFlare.prototype.updateLensFlares = function() {
var f, fl = this.lensFlares.length;
var flare;
......@@ -66,13 +66,8 @@ THREE.LensFlare.prototype.updateLensFlares = function( visibility ) {
flare.x = this.positionScreen.x + vecX * flare.distance;
flare.y = this.positionScreen.y + vecY * flare.distance;
flare.wantedScale = ( visibility * 0.2 + 0.8 );
flare.wantedRotation = flare.x * Math.PI * 0.25;
flare.wantedOpacity = visibility;
flare.scale += ( flare.wantedScale - flare.scale ) * 0.25;
flare.rotation += ( flare.wantedRotation - flare.rotation ) * 0.25;
flare.opacity += ( flare.wantedOpacity - flare.opacity ) * 0.5;
}
......
......@@ -161,7 +161,7 @@ THREE.WebGLRenderer = function ( parameters ) {
_lensFlare.vertexBuffer = _gl.createBuffer();
_lensFlare.elementBuffer = _gl.createBuffer();
_lensFlare.tempTexture = _gl.createTexture();
_lensFlare.readBackPixels = new Uint8Array( 16*16*4 );
_lensFlare.occlusionTexture = _gl.createTexture();
_gl.bindBuffer( _gl.ARRAY_BUFFER, _lensFlare.vertexBuffer );
_gl.bufferData( _gl.ARRAY_BUFFER, _lensFlare.vertices, _gl.STATIC_DRAW );
......@@ -176,23 +176,47 @@ THREE.WebGLRenderer = function ( parameters ) {
_gl.texParameteri( _gl.TEXTURE_2D, _gl.TEXTURE_MAG_FILTER, _gl.NEAREST );
_gl.texParameteri( _gl.TEXTURE_2D, _gl.TEXTURE_MIN_FILTER, _gl.NEAREST );
_lensFlare.program = _gl.createProgram();
_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 = _gl.createProgram();
_gl.attachShader( _lensFlare.program, getShader( "fragment", THREE.ShaderLib.lensFlare.fragmentShader ));
_gl.attachShader( _lensFlare.program, getShader( "vertex", THREE.ShaderLib.lensFlare.vertexShader ));
_gl.linkProgram( _lensFlare.program );
} else {
_lensFlare.hasVertexTexture = true;
_lensFlare.program = _gl.createProgram();
_gl.attachShader( _lensFlare.program, getShader( "fragment", THREE.ShaderLib.lensFlareVertexTexture.fragmentShader ));
_gl.attachShader( _lensFlare.program, getShader( "vertex", THREE.ShaderLib.lensFlareVertexTexture.vertexShader ));
_gl.linkProgram( _lensFlare.program );
}
_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.scale = _gl.getUniformLocation( _lensFlare.program, "scale" );
_lensFlare.uniforms.rotation = _gl.getUniformLocation( _lensFlare.program, "rotation" );
_lensFlare.uniforms.screenPosition = _gl.getUniformLocation( _lensFlare.program, "screenPosition" );
_lensFlare.uniforms.renderPink = _gl.getUniformLocation( _lensFlare.program, "renderPink" );
this.setSize = function ( width, height ) {
......@@ -3175,6 +3199,7 @@ THREE.WebGLRenderer = function ( parameters ) {
}
/*
* Render lens flares
* Method: renders 16x16 0xff00ff-colored points scattered over the light source area,
......@@ -3215,24 +3240,24 @@ THREE.WebGLRenderer = function ( parameters ) {
// loop through all lens flares to update their occlusion and positions
// setup gl and common used attribs/unforms
_gl.uniform1i( uniforms.map, 0 );
_gl.activeTexture( _gl.TEXTURE0 );
_gl.uniform1f( uniforms.opacity, 1 );
_gl.uniform1f( uniforms.rotation, 0 );
_gl.uniform2fv( uniforms.scale, scale );
_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.bindTexture( _gl.TEXTURE_2D, _lensFlare.tempTexture );
_gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, _lensFlare.elementBuffer );
_gl.disable( _gl.CULL_FACE );
_gl.depthMask( false );
_gl.activeTexture( _gl.TEXTURE0 );
_gl.bindTexture( _gl.TEXTURE_2D, _lensFlare.occlusionTexture );
_gl.activeTexture( _gl.TEXTURE1 );
_gl.bindTexture( _gl.TEXTURE_2D, _lensFlare.tempTexture );
for ( o = 0; o < ol; o ++ ) {
......@@ -3257,73 +3282,48 @@ THREE.WebGLRenderer = function ( parameters ) {
screenPositionPixels[ 1 ] = screenPosition[ 1 ] * halfViewportHeight + halfViewportHeight;
// screen cull or go on if vertex texture
if( _lensFlare.hasVertexTexture ||
( screenPositionPixels[ 0 ] > 0 &&
screenPositionPixels[ 0 ] < _viewportWidth &&
screenPositionPixels[ 1 ] > 0 &&
screenPositionPixels[ 1 ] < _viewportHeight )) {
// save current RGB to temp texture
_gl.bindTexture( _gl.TEXTURE_2D, _lensFlare.tempTexture );
_gl.copyTexSubImage2D( _gl.TEXTURE_2D, 0, 0, 0, screenPositionPixels[ 0 ] - 8, screenPositionPixels[ 1 ] - 8, 16, 16 );
// render pink quad
_gl.uniform1i( uniforms.renderType, 0 );
_gl.uniform2fv( uniforms.scale, scale );
_gl.uniform3fv( uniforms.screenPosition, screenPosition );
_gl.uniform1i( uniforms.renderPink, 1 );
_gl.disable( _gl.BLEND );
_gl.enable( _gl.DEPTH_TEST );
_gl.drawElements( _gl.TRIANGLES, 6, _gl.UNSIGNED_SHORT, 0 );
// read back
try {
_gl.readPixels( screenPositionPixels[ 0 ] - 8, screenPositionPixels[ 1 ] - 8, 16, 16, _gl.RGBA, _gl.UNSIGNED_BYTE, _lensFlare.readBackPixels );
} catch( error ) {
// copy result to occlusionMap
console.log( "WebGLRenderer.renderLensFlare: readPixels failed!" );
}
if ( _gl.getError() ) {
console.log( "WebGLRenderer.renderLensFlare: readPixels failed!" );
}
// sample read back pixels
sampleDistance = parseInt( 5 * ( 1 - Math.max( 0, Math.min( -objectZ, camera.far )) / camera.far ), 10 ) + 2;
sampleX = sampleDistance * 4;
sampleY = sampleDistance * 4 * 16;
visibility = 0;
sampleIndex = ( sampleMidX - sampleX ) + ( sampleMidY - sampleY ); // upper left
if( _lensFlare.readBackPixels[ sampleIndex + 0 ] === 255 &&
_lensFlare.readBackPixels[ sampleIndex + 1 ] === 0 &&
_lensFlare.readBackPixels[ sampleIndex + 2 ] === 255 ) visibility += 0.2;
_gl.bindTexture( _gl.TEXTURE_2D, _lensFlare.occlusionTexture );
_gl.copyTexSubImage2D( _gl.TEXTURE_2D, 0, 0, 0, screenPositionPixels[ 0 ] - 8, screenPositionPixels[ 1 ] - 8, 16, 16 );
sampleIndex = ( sampleMidX + sampleX ) + ( sampleMidY - sampleY ); // upper right
if( readBackPixels[ sampleIndex + 0 ] === 255 &&
readBackPixels[ sampleIndex + 1 ] === 0 &&
readBackPixels[ sampleIndex + 2 ] === 255 ) visibility += 0.2;
sampleIndex = ( sampleMidX + sampleX ) + ( sampleMidY + sampleY ); // lower right
if( readBackPixels[ sampleIndex + 0 ] === 255 &&
readBackPixels[ sampleIndex + 1 ] === 0 &&
readBackPixels[ sampleIndex + 2 ] === 255 ) visibility += 0.2;
// restore graphics
sampleIndex = ( sampleMidX - sampleX ) + ( sampleMidY + sampleY ); // lower left
if( readBackPixels[ sampleIndex + 0 ] === 255 &&
readBackPixels[ sampleIndex + 1 ] === 0 &&
readBackPixels[ sampleIndex + 2 ] === 255 ) visibility += 0.2;
_gl.uniform1i( uniforms.renderType, 1 );
_gl.disable( _gl.DEPTH_TEST );
_gl.bindTexture( _gl.TEXTURE_2D, _lensFlare.tempTexture );
_gl.drawElements( _gl.TRIANGLES, 6, _gl.UNSIGNED_SHORT, 0 );
sampleIndex = sampleMidX + sampleMidY; // center
if( readBackPixels[ sampleIndex + 0 ] === 255 &&
readBackPixels[ sampleIndex + 1 ] === 0 &&
readBackPixels[ sampleIndex + 2 ] === 255 ) visibility += 0.2;
// update object positions
object.positionScreen.x = screenPosition[ 0 ];
object.positionScreen.y = screenPosition[ 1 ];
......@@ -3331,30 +3331,19 @@ THREE.WebGLRenderer = function ( parameters ) {
if ( object.customUpdateCallback ) {
object.customUpdateCallback( visibility, object );
object.customUpdateCallback( object );
} else {
object.updateLensFlares( visibility );
object.updateLensFlares();
}
// restore graphics
// render flares
_gl.uniform1i( uniforms.renderPink, 0 );
_gl.disable( _gl.DEPTH_TEST );
_gl.drawElements( _gl.TRIANGLES, 6, _gl.UNSIGNED_SHORT, 0 );
}
// loop through all lens flares and draw their flares
// setup gl
for ( o = 0; o < ol; o ++ ) {
object = scene.__webglLensFlares[ o ].object;
_gl.uniform1i( uniforms.renderType, 2 );
_gl.enable( _gl.BLEND );
for ( f = 0, fl = object.lensFlares.length; f < fl; f ++ ) {
......@@ -3371,14 +3360,12 @@ THREE.WebGLRenderer = function ( parameters ) {
scale[ 1 ] = size;
_gl.uniform3fv( uniforms.screenPosition, screenPosition );
_gl.uniform1f( uniforms.rotation, flare.rotation );
_gl.uniform2fv( uniforms.scale, scale );
_gl.uniform1f( uniforms.rotation, flare.rotation );
_gl.uniform1f( uniforms.opacity, flare.opacity );
setBlending( flare.blending );
setTexture( flare.texture, 0 );
// todo: only draw if loaded
setTexture( flare.texture, 1 );
_gl.drawElements( _gl.TRIANGLES, 6, _gl.UNSIGNED_SHORT, 0 );
......@@ -3388,6 +3375,8 @@ THREE.WebGLRenderer = function ( parameters ) {
}
}
// restore gl
_gl.enable( _gl.CULL_FACE );
......@@ -3397,6 +3386,8 @@ THREE.WebGLRenderer = function ( parameters ) {
}
function setupMatrices( object, camera ) {
object._modelViewMatrix.multiplyToArray( camera.matrixWorldInverse, object.matrixWorld, object._modelViewMatrixArray );
......
......@@ -569,6 +569,95 @@ THREE.UniformsLib = {
THREE.ShaderLib = {
'lensFlareVertexTexture': {
vertexShader: [
"uniform vec3 screenPosition;",
"uniform vec2 scale;",
"uniform float rotation;",
"uniform int renderType;",
"uniform sampler2D occlusionMap;",
"attribute vec2 position;",
"attribute vec2 UV;",
"varying vec2 vUV;",
"varying float vVisibility;",
"void main(void)",
"{",
"vUV = UV;",
"vec2 pos = position;",
"if( renderType == 2 ) {",
"vec4 visibility = texture2D( occlusionMap, vec2( 0.1, 0.1 )) +",
"texture2D( occlusionMap, vec2( 0.5, 0.1 )) +",
"texture2D( occlusionMap, vec2( 0.9, 0.1 )) +",
"texture2D( occlusionMap, vec2( 0.9, 0.5 )) +",
"texture2D( occlusionMap, vec2( 0.9, 0.9 )) +",
"texture2D( occlusionMap, vec2( 0.5, 0.9 )) +",
"texture2D( occlusionMap, vec2( 0.1, 0.9 )) +",
"texture2D( occlusionMap, vec2( 0.1, 0.5 )) +",
"texture2D( occlusionMap, vec2( 0.5, 0.5 ));",
"vVisibility = ( visibility.r / 9.0 ) *",
"( 1.0 - visibility.g / 9.0 ) *",
"( visibility.b / 9.0 ) *",
"( 1.0 - visibility.a / 9.0 );",
"pos.x = cos( rotation ) * position.x - sin( rotation ) * position.y;",
"pos.y = sin( rotation ) * position.x + cos( rotation ) * position.y;",
"}",
"gl_Position = vec4(( pos * scale + screenPosition.xy ).xy, screenPosition.z, 1.0 );",
"}"
].join( "\n" ),
fragmentShader: [
"#ifdef GL_ES",
"precision highp float;",
"#endif",
"uniform sampler2D map;",
"uniform float opacity;",
"uniform int renderType;",
"varying vec2 vUV;",
"varying float vVisibility;",
"void main( void )",
"{",
// pink square
"if( renderType == 0 ) {",
"gl_FragColor = vec4( 1.0, 0.0, 1.0, 0.0 );",
// restore
"} else if( renderType == 1 ) {",
"gl_FragColor = texture2D( map, vUV );",
// flare
"} else {",
"vec4 color = texture2D( map, vUV );",
"color.a *= opacity * vVisibility;",
"gl_FragColor = color;",
"}",
"}"
].join( "\n" )
},
'lensFlare': {
vertexShader: [
......@@ -576,17 +665,25 @@ THREE.ShaderLib = {
"uniform vec3 screenPosition;",
"uniform vec2 scale;",
"uniform float rotation;",
"uniform int renderType;",
"attribute vec2 position;",
"attribute vec2 UV;",
"varying vec2 vUV;",
"void main(void)",
"{",
"vUV = UV;",
"vec2 pos;",
"vec2 pos = position;",
"if( renderType == 2 ) {",
"pos.x = cos( rotation ) * position.x - sin( rotation ) * position.y;",
"pos.y = sin( rotation ) * position.x + cos( rotation ) * position.y;",
"}",
"gl_Position = vec4(( pos * scale + screenPosition.xy ).xy, screenPosition.z, 1.0 );",
"}"
......@@ -599,18 +696,39 @@ THREE.ShaderLib = {
"#endif",
"uniform sampler2D map;",
"uniform sampler2D occlusionMap;",
"uniform float opacity;",
"uniform int renderType;",
"uniform int renderPink;",
"varying vec2 vUV;",
"void main( void )",
"{",
"if( renderPink == 1 ) {",
"gl_FragColor = vec4( 1.0, 0.0, 1.0, 1.0 );",
// pink square
"if( renderType == 0 ) {",
"gl_FragColor = vec4( 1.0, 0.0, 1.0, 0.0 );",
// restore
"} else if( renderType == 1 ) {",
"gl_FragColor = texture2D( map, vUV );",
// flare
"} else {",
"float visibility = texture2D( occlusionMap, vec2( 0.5, 0.1 )).a +",
"texture2D( occlusionMap, vec2( 0.9, 0.5 )).a +",
"texture2D( occlusionMap, vec2( 0.5, 0.9 )).a +",
"texture2D( occlusionMap, vec2( 0.1, 0.5 )).a;",
"visibility = ( 1.0 - visibility / 4.0 );",
"vec4 color = texture2D( map, vUV );",
"color.a *= opacity;",
"color.a *= opacity * visibility;",
"gl_FragColor = color;",
"}",
"}"
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册