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

Added LensFlares. Note that a new blending mode has been added. Added an...

Added LensFlares. Note that a new blending mode has been added. Added an (quite ugly example). NOTE: this doesn't seem to work on Chrome+Mac+NVIDIA unless you turn off antialias (which I've done in the example).
上级 9ee4c267
因为 它太大了无法显示 source diff 。你可以改为 查看blob
......@@ -150,7 +150,6 @@
lightCube.position.copy( light.position );
lightCube.position.multiplyScalar( 200 );
t += 0.02;
camera.position.x += ( mouseX - camera.position.x ) * .05;
......
<!DOCTYPE HTML>
<html lang="en">
<head>
<title>three.js - webgl</title>
<meta charset="utf-8">
<style type="text/css">
body {
background:#fff;
padding:0;
margin:0;
font-weight: bold;
overflow:hidden;
}
</style>
</head>
<body>
<script type="text/javascript" src="../build/Three.js"></script>
<script type="text/javascript" src="js/Stats.js"></script>
<script type="text/javascript">
var statsEnabled = true;
var container, stats;
var camera, scene, renderer;
var mesh, boxMesh, light, lightCube, light2, lightCube2, zmesh, lightMesh, geometry;
var mouseX = 0, mouseY = 0;
var windowHalfX = window.innerWidth / 2;
var windowHalfY = window.innerHeight / 2;
document.addEventListener( 'mousemove', onDocumentMouseMove, false );
init();
function init() {
container = document.createElement( 'div' );
document.body.appendChild( container );
camera = new THREE.Camera( 60, window.innerWidth / window.innerHeight, 1, 1000 );
camera.position.z = 250;
scene = new THREE.Scene();
// world
var cube = new Cube( 300, 300, 10 );
var material0 = new THREE.MeshPhongMaterial( { color:0xff00ff } );
var material1 = new THREE.MeshLambertMaterial( { color:0x00ff00 } );
var material2 = new THREE.MeshLambertMaterial( { color:0x0000ff } );
var mesh1 = new THREE.Mesh( cube, material0 );
mesh1.position.z = -150;
scene.addChild( mesh1 );
var mesh2 = new THREE.Mesh( cube, material1 );
mesh2.position.x = -150;
mesh2.rotation.y = 90 * Math.PI / 180;
scene.addChild( mesh2 );
var mesh3 = new THREE.Mesh( cube, material2 );
mesh3.position.y = -150;
mesh3.rotation.x = 90 * Math.PI / 180;
scene.addChild( mesh3 );
new THREE.ShadowVolume( mesh1 )
new THREE.ShadowVolume( mesh2 )
new THREE.ShadowVolume( mesh3 )
// moving objects
var cube = new Cube( 40, 40, 40 );
var torus = new Torus( 40, 10 );
var sphere = new Sphere( 40 );
var cylinder = new Cylinder( 10, 10, 20, 40, 0, 0 );
mesh = new THREE.Mesh( torus, material1 );
scene.addChild( mesh );
boxMesh = new THREE.Mesh( cube, material2 );
scene.addChild( boxMesh );
new THREE.ShadowVolume( mesh );
new THREE.ShadowVolume( boxMesh );
// lights
light = new THREE.PointLight( 0xffffff );
scene.addChild( light );
light = new THREE.DirectionalLight( 0xffffff );
light.position.set( 0, 1, 0 );
scene.addChild( light );
var cube = new Sphere( 2 );
lightCube = new THREE.Mesh( cube, material2 );
lightCube.visible = false;
scene.addChild( lightCube );
var lensFlare = new THREE.LensFlare( ImageUtils.loadTexture( "textures/lensflare0.png" ), 128, 0.0, THREE.AdditiveAlphaBlending );
lensFlare.add( ImageUtils.loadTexture( "textures/lensflare1.png" ), 256, 0.33, THREE.AdditiveAlphaBlending );
lensFlare.add( lensFlare.lensFlares[ 1 ].texture, 300, 0.66, THREE.AdditiveAlphaBlending );
lensFlare.add( lensFlare.lensFlares[ 1 ].texture, 400, 1.0, THREE.AdditiveAlphaBlending );
lightCube.addChild( lensFlare );
// renderer
renderer = new THREE.WebGLRenderer( { antialias: false } );
renderer.setClearColorHex( 0x222222, 1 );
renderer.setSize( window.innerWidth, window.innerHeight );
container.appendChild( renderer.domElement );
if ( statsEnabled ) {
stats = new Stats();
stats.domElement.style.position = 'absolute';
stats.domElement.style.top = '0px';
stats.domElement.style.zIndex = 100;
container.appendChild( stats.domElement );
}
setInterval( loop, 1000 / 60 );
}
function onDocumentMouseMove(event) {
mouseX = ( event.clientX - windowHalfX );
mouseY = ( event.clientY - windowHalfY );
}
var t = 0;
function loop() {
mesh.position.x = Math.sin( t ) * 100;
mesh.position.y = Math.cos( t ) * 100;
mesh.rotation.x += 0.5 * Math.PI / 180;
mesh.rotation.y += 1.0 * Math.PI / 180;
mesh.rotation.z += 1.5 * Math.PI / 180;
boxMesh.position.z = Math.sin( t ) * 100;
boxMesh.rotation.x = Math.sin( t ) * 180 * Math.PI / 180;
light.position.x = Math.sin( t );
light.position.y = 0.5;
light.position.normalize();
lightCube.position.copy( light.position );
lightCube.position.multiplyScalar( 200 );
t += 0.015;
camera.position.x += ( mouseX - camera.position.x ) * .05;
camera.position.y += ( - mouseY - camera.position.y ) * .05;
renderer.render( scene, camera );
if ( statsEnabled ) stats.update();
}
function log( text ) {
var e = document.getElementById("log");
e.innerHTML = text + "<br/>" + e.innerHTML;
}
</script>
</body>
</html>
......@@ -2,7 +2,7 @@
* @author Mikael Emtinger
*/
THREE.LensFlare = function ( texture, size ) {
THREE.LensFlare = function ( texture, size, distance, blending ) {
THREE.Object3D.call( this );
......@@ -10,7 +10,11 @@ THREE.LensFlare = function ( texture, size ) {
this.lensFlares = [];
this.customUpdateCallback = undefined;
this.add( texture, size, 0 );
if( texture !== undefined ) {
this.add( texture, size, distance, blending );
}
};
THREE.LensFlare.prototype = new THREE.Object3D();
......@@ -22,11 +26,23 @@ THREE.LensFlare.prototype.supr = THREE.Object3D.prototype;
* Add: adds another flare
*/
THREE.LensFlare.prototype.add = function( texture, size, distance ) {
THREE.LensFlare.prototype.add = function( texture, size, distance, blending ) {
if( size === undefined ) size = -1;
if( distance === undefined ) distance = 0;
if( blending === undefined ) blending = THREE.BillboardBlending;
distance = Math.min( distance, Math.max( 0, distance ));
this.lensFlares.push( { texture: texture, size: size, distance: distance, position: new THREE.Vector3() } );
this.lensFlares.push( { texture: texture, // THREE.Texture
size: size, // size in pixels (-1 = use texture.width)
distance: distance, // distance (0-1) from light source (0=at light source)
x: 0, y: 0, z: 0, // screen position (-1 => 1) z = 0 is ontop z = 1 is back
scale: 1, // scale
rotation: 1, // rotation
opacity: 1, // opacity
blending: blending } ); // blending
}
......@@ -35,9 +51,31 @@ THREE.LensFlare.prototype.add = function( texture, size, distance ) {
* Set myLensFlare.customUpdateCallback to alter the flares in your project specific way.
*/
THREE.LensFlare.updateLensFlares = function() {
THREE.LensFlare.prototype.updateLensFlares = function( visibility ) {
var f, fl = this.lensFlares.length;
var flare;
var vecX = -this.positionScreen.x * 2;
var vecY = -this.positionScreen.y * 2;
// todo: update lens halo positions here
for( f = 0; f < fl; f++ ) {
flare = this.lensFlares[ f ];
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;
}
}
......
......@@ -15,5 +15,6 @@ THREE.AdditiveBlending = 1;
THREE.SubtractiveBlending = 2;
THREE.BillboardBlending = 3;
THREE.ReverseSubtractiveBlending = 4;
THREE.AdditiveAlphaBlending = 5;
THREE.MaterialCounter = { value: 0 };
......@@ -160,6 +160,7 @@ THREE.WebGLRenderer = function ( parameters ) {
_lensFlare.vertexBuffer = _gl.createBuffer();
_lensFlare.elementBuffer = _gl.createBuffer();
_lensFlare.tempTexture = _gl.createTexture();
_lensFlare.readBackPixels = new Uint8Array( 16*16*4 );
_gl.bindBuffer( _gl.ARRAY_BUFFER, _lensFlare.vertexBuffer );
_gl.bufferData( _gl.ARRAY_BUFFER, _lensFlare.vertices, _gl.STATIC_DRAW );
......@@ -193,119 +194,6 @@ THREE.WebGLRenderer = function ( parameters ) {
_lensFlare.uniforms.screenPosition = _gl.getUniformLocation( _lensFlare.program, "screenPosition" );
_lensFlare.uniforms.renderPink = _gl.getUniformLocation( _lensFlare.program, "renderPink" );
/*
* Render lens flares
* Method: renders 9 0xff00ff-colored points scattered over the light source area,
* reads these back and calculates occlusion.
* Then LensFlare.updateLensFlares() is called to re-position and
* update transparency of flares. Then they are rendered.
*
*/
function renderLensFlares( scene, camera, renderTarget ) {
var object, geometryGroup, material;
var o, ol = scene.__webglLensFlares.length;
var tempPosition = new THREE.Vector3();
var invAspect = _viewportHeight / _viewportWidth;
var halfViewportWidth = _viewportWidth * 0.5;
var halfViewportHeight = _viewportHeight * 0.5;
var size = 16 / _viewportHeight;
var restoreScale = [ size * invAspect, size ];
var screenPosition = [ 1, 1, 0 ];
var screenPositionPixels = [ 1, 1 ];
// set lensflare program
if( _oldProgram !== _lensFlare.program ) {
_gl.useProgram( _lensFlare.program );
_oldProgram = _lensFlare.program;
}
// loop through all lens flares to update their occlusion and positions
// setup gl and common used attribs/unforms
_gl.uniform1i( _lensFlare.uniforms.map, 0 );
_gl.activeTexture( _gl.TEXTURE0 );
_gl.uniform1f( _lensFlare.uniforms.opacity, 1 );
_gl.uniform1f( _lensFlare.uniforms.rotation, 0 );
_gl.uniform2fv( _lensFlare.uniforms.scale, restoreScale );
_gl.bindBuffer( _gl.ARRAY_BUFFER, _lensFlare.vertexBuffer );
_gl.vertexAttribPointer( _lensFlare.attributes.vertex, 2, _gl.FLOAT, false, 2 * 8, 0 );
_gl.vertexAttribPointer( _lensFlare.attributes.uv, 2, _gl.FLOAT, false, 2 * 8, 8 );
_gl.bindTexture( _gl.TEXTURE_2D, _lensFlare.tempTexture );
_gl.cullFace( _gl.BACK );
_gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, _lensFlare.elementBuffer );
for( o = 0; o < ol; o++ ) {
// calc object screen position
object = scene.__webglLensFlares[ o ].object;
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[ 0 ] = tempPosition.x;
screenPosition[ 1 ] = tempPosition.y;
screenPosition[ 2 ] = tempPosition.z;
screenPositionPixels[ 0 ] = screenPosition[ 0 ] * halfViewportWidth + halfViewportWidth;
screenPositionPixels[ 1 ] = screenPosition[ 1 ] * halfViewportHeight + halfViewportHeight;
// todo: viewport culling
// save current RGB to temp texture
_gl.copyTexSubImage2D( _gl.TEXTURE_2D, 0, 0, 0, screenPositionPixels[ 0 ] - 8, screenPositionPixels[ 1 ] - 8, 16, 16 );
// render pink quad
_gl.uniform3fv( _lensFlare.uniforms.screenPosition, screenPosition );
_gl.uniform1i( _lensFlare.uniforms.renderPink, 1 );
_gl.drawElements( _gl.TRIANGLES, 6, _gl.UNSIGNED_SHORT, 0 );
// restore graphics
screenPosition[ 2 ] = 0;
_gl.uniform1i( _lensFlare.uniforms.renderPink, 0 );
_gl.uniform3fv( _lensFlare.uniforms.screenPosition, screenPosition );
_gl.disable( _gl.DEPTH_TEST );
_gl.depthMask( false );
_gl.drawElements( _gl.TRIANGLES, 6, _gl.UNSIGNED_SHORT, 0 );
// copy existing -> temp texture
// render pink quad
// read back pixles
// copy temp -> back
// call flare update
// render flares
}
}
......@@ -2712,15 +2600,6 @@ THREE.WebGLRenderer = function ( parameters ) {
}
// render flares
if( scene.__webglLensFlares.length ) {
renderLensFlares( scene, camera, renderTarget );
}
// render stencil shadows
if( stencil && scene.__webglShadowVolumes.length && scene.lights.length ) {
......@@ -2729,6 +2608,14 @@ THREE.WebGLRenderer = function ( parameters ) {
}
// render lens flares
if( scene.__webglLensFlares.length ) {
renderLensFlares( scene, camera );
}
// Generate mipmap if we're using any kind of mipmap filtering
......@@ -2882,6 +2769,229 @@ THREE.WebGLRenderer = function ( parameters ) {
_gl.depthMask( true );
}
/*
* Render lens flares
* Method: renders 16x16 0xff00ff-colored points scattered over the light source area,
* reads these back and calculates occlusion.
* Then LensFlare.updateLensFlares() is called to re-position and
* update transparency of flares. Then they are rendered.
*
*/
function renderLensFlares( scene, camera ) {
var object, objectZ, geometryGroup, material;
var o, ol = scene.__webglLensFlares.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 = [ size * invAspect, size ];
var screenPosition = [ 1, 1, 0 ];
var screenPositionPixels = [ 1, 1 ];
var sampleX, sampleY, readBackPixels = _lensFlare.readBackPixels;
var sampleMidX = 7 * 4;
var sampleMidY = 7 * 16 * 4;
var sampleIndex, visibility;
var uniforms = _lensFlare.uniforms;
var attributes = _lensFlare.attributes;
// set lensflare program and reset blending
_gl.useProgram( _lensFlare.program );
_oldProgram = _lensFlare.program;
_oldBlending = "";
// 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.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 );
for( o = 0; o < ol; o++ ) {
// calc object screen position
object = scene.__webglLensFlares[ o ].object;
tempPosition.set( object.matrixWorld.n14, object.matrixWorld.n24, object.matrixWorld.n34 );
camera.matrixWorldInverse.multiplyVector3( tempPosition );
objectZ = tempPosition.z;
camera.projectionMatrix.multiplyVector3( tempPosition );
// setup arrays for gl programs
screenPosition[ 0 ] = tempPosition.x;
screenPosition[ 1 ] = tempPosition.y;
screenPosition[ 2 ] = tempPosition.z;
screenPositionPixels[ 0 ] = screenPosition[ 0 ] * halfViewportWidth + halfViewportWidth;
screenPositionPixels[ 1 ] = screenPosition[ 1 ] * halfViewportHeight + halfViewportHeight;
// save current RGB to temp texture
_gl.copyTexSubImage2D( _gl.TEXTURE_2D, 0, 0, 0, screenPositionPixels[ 0 ] - 8, screenPositionPixels[ 1 ] - 8, 16, 16 );
// render pink quad
_gl.uniform3fv( uniforms.screenPosition, screenPosition );
_gl.uniform1i( uniforms.renderPink, 1 );
_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 ) {
console.log( "WebGLRenderer.renderLensFlare: readPixels failed!" );
}
if( _gl.getError()) {
console.log( "WebGLRenderer.renderLensFlare: readPixels failed!" );
}
// sample read back pixels
sampleDistance = parseInt( 6 * ( 1 - Math.max( 0, Math.min( -objectZ, camera.far )) / camera.far ), 10 ) + 1;
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;
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;
sampleIndex = ( sampleMidX - sampleX ) + ( sampleMidY + sampleY ); // lower left
if( readBackPixels[ sampleIndex + 0 ] === 255 &&
readBackPixels[ sampleIndex + 1 ] === 0 &&
readBackPixels[ sampleIndex + 2 ] === 255 ) visibility += 0.2;
sampleIndex = sampleMidX + sampleMidY; // center
if( readBackPixels[ sampleIndex + 0 ] === 255 &&
readBackPixels[ sampleIndex + 1 ] === 0 &&
readBackPixels[ sampleIndex + 2 ] === 255 ) visibility += 0.2;
object.positionScreen.x = screenPosition[ 0 ];
object.positionScreen.y = screenPosition[ 1 ];
object.positionScreen.z = screenPosition[ 2 ];
if( object.customUpdateCallback ) {
object.customUpdateCallback( visibility, object );
} else {
object.updateLensFlares( visibility );
}
// restore graphics
_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
_gl.enable( _gl.BLEND );
for( o = 0; o < ol; o++ ) {
object = scene.__webglLensFlares[ o ].object;
for( f = 0, fl = object.lensFlares.length; f < fl; f++ ) {
flare = object.lensFlares[ f ];
if( flare.opacity > 0.001 && flare.scale > 0.001 ) {
screenPosition[ 0 ] = flare.x;
screenPosition[ 1 ] = flare.y;
screenPosition[ 2 ] = flare.z;
size = flare.size * flare.scale / _viewportHeight;
scale[ 0 ] = size * invAspect;
scale[ 1 ] = size;
_gl.uniform3fv( uniforms.screenPosition, screenPosition );
_gl.uniform1f( uniforms.rotation, flare.rotation );
_gl.uniform2fv( uniforms.scale, scale );
_gl.uniform1f( uniforms.opacity, flare.opacity );
setBlending( flare.blending );
setTexture( flare.texture, 0 );
// todo: only draw if loaded
_gl.drawElements( _gl.TRIANGLES, 6, _gl.UNSIGNED_SHORT, 0 );
}
}
}
// restore gl
_gl.enable( _gl.CULL_FACE );
_gl.enable( _gl.DEPTH_TEST );
_gl.depthMask( true );
_gl.disable( _gl.BLEND );
}
function setupMatrices ( object, camera ) {
......@@ -3519,6 +3629,13 @@ THREE.WebGLRenderer = function ( parameters ) {
switch ( blending ) {
case THREE.AdditiveAlphaBlending:
_gl.blendEquation( _gl.FUNC_ADD );
_gl.blendFunc( _gl.SRC_ALPHA, _gl.ONE );
break;
case THREE.AdditiveBlending:
_gl.blendEquation( _gl.FUNC_ADD );
......
......@@ -586,6 +586,8 @@ THREE.ShaderLib = {
"#endif",
"uniform sampler2D map;",
"uniform float opacity;",
"uniform int renderPink;",
"varying vec2 vUV;",
......@@ -594,7 +596,9 @@ THREE.ShaderLib = {
"if( renderPink == 1 ) {",
"gl_FragColor = vec4( 1.0, 0.0, 1.0, 1.0 );",
"} else {",
"gl_FragColor = texture2D( map, vUV );",
"vec4 color = texture2D( map, vUV );",
"color.a *= opacity;",
"gl_FragColor = color;",
"}",
"}"
].join( "\n" )
......
......@@ -36,6 +36,7 @@ COMMON_FILES = [
'lights/AmbientLight.js',
'lights/DirectionalLight.js',
'lights/PointLight.js',
'lights/LensFlare.js',
'materials/Material.js',
'materials/Mappings.js',
'materials/LineBasicMaterial.js',
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册