瀏覽代碼

add TAARenderPass and polish MSAA example/code.

Ben Houston 9 年之前
父節點
當前提交
64eb745c3c

+ 7 - 5
examples/js/postprocessing/ManualMSAARenderPass.js

@@ -18,16 +18,16 @@ THREE.ManualMSAARenderPass = function ( scene, camera, params ) {
 
 	if ( THREE.CompositeShader === undefined ) {
 
-		console.error( "THREE.MSAAPass relies on THREE.CompositeShader" );
+		console.error( "THREE.ManualMSAARenderPass relies on THREE.CompositeShader" );
 
 	}
 
 	var compositeShader = THREE.CompositeShader;
-	this.uniforms = THREE.UniformsUtils.clone( compositeShader.uniforms );
+	this.compositeUniforms = THREE.UniformsUtils.clone( compositeShader.uniforms );
 
 	this.materialComposite = new THREE.ShaderMaterial(	{
 
-		uniforms: this.uniforms,
+		uniforms: this.compositeUniforms,
 		vertexShader: compositeShader.vertexShader,
 		fragmentShader: compositeShader.fragmentShader,
 		transparent: true,
@@ -49,6 +49,8 @@ THREE.ManualMSAARenderPass = function ( scene, camera, params ) {
 
 THREE.ManualMSAARenderPass.prototype = {
 
+	constructor: THREE.ManualMSAARenderPass,
+
 	dispose: function() {
 
 		if ( this.sampleRenderTarget ) {
@@ -88,8 +90,8 @@ THREE.ManualMSAARenderPass.prototype = {
 		var autoClear = renderer.autoClear;
 		renderer.autoClear = false;
 
-		this.uniforms[ "scale" ].value = 1.0 / ( jitterOffsets.length );
-		this.uniforms[ "tForeground" ].value = this.sampleRenderTarget;
+		this.compositeUniforms[ "scale" ].value = 1.0 / ( jitterOffsets.length );
+		this.compositeUniforms[ "tForeground" ].value = this.sampleRenderTarget;
 
 		// render the scene multiple times, each slightly jitter offset from the last and accumulate the results.
 		for ( var i = 0; i < jitterOffsets.length; i ++ ) {

+ 114 - 0
examples/js/postprocessing/TAARenderPass.js

@@ -0,0 +1,114 @@
+/**
+ * @author bhouston / http://clara.io/ *
+ */
+
+THREE.TAARenderPass = function ( scene, camera, params ) {
+
+	if ( THREE.ManualMSAARenderPass === undefined ) {
+
+		console.error( "THREE.TAARenderPass relies on THREE.ManualMSAARenderPass" );
+
+	}
+	THREE.ManualMSAARenderPass.call( this, scene, camera, params );
+
+	this.sampleLevel = 1;
+	this.accumulate = false;
+
+	if ( THREE.CompositeShader === undefined ) {
+
+		console.error( "THREE.TAARenderPass relies on THREE.CompositeShader" );
+
+	}
+
+	var accumulateShader = THREE.CompositeShader;
+	this.accumulateUniforms = THREE.UniformsUtils.clone( accumulateShader.uniforms );
+
+	this.materialAccumulate = new THREE.ShaderMaterial(	{
+
+		uniforms: this.accumulateUniforms,
+		vertexShader: accumulateShader.vertexShader,
+		fragmentShader: accumulateShader.fragmentShader,
+		transparent: true,
+		blending: THREE.CustomBlending,
+		blendSrc: THREE.OneFactor,
+		blendDst: THREE.OneMinusSrcAlphaFactor,
+		blendEquation: THREE.AddEquation,
+		depthTest: false,
+		depthWrite: false
+
+	} );
+
+	this.camera3 = new THREE.OrthographicCamera( - 1, 1, 1, - 1, 0, 1 );
+	this.scene3	= new THREE.Scene();
+	this.quad3 = new THREE.Mesh( new THREE.PlaneGeometry( 2, 2 ), this.materialAccumulate );
+	this.scene3.add( this.quad3 );
+
+};
+
+THREE.TAARenderPass.prototype = Object.create( THREE.ManualMSAARenderPass.prototype );
+THREE.TAARenderPass.prototype.constructor = THREE.TAARenderPass;
+THREE.TAARenderPass.JitterVectors = THREE.ManualMSAARenderPass.JitterVectors;
+
+THREE.TAARenderPass.prototype.render = function ( renderer, writeBuffer, readBuffer, delta ) {
+
+	if( ! this.accumulate ) {
+
+			THREE.ManualMSAARenderPass.prototype.render.call( this, renderer, writeBuffer, readBuffer, delta );
+
+			this.accumulateIndex = 0;
+			return;
+
+	}
+
+	var jitterOffsets = THREE.TAARenderPass.JitterVectors[ 4 ];
+
+	var camera = ( this.camera || this.scene.camera );
+
+	if ( ! this.sampleRenderTarget ) {
+
+		this.sampleRenderTarget = new THREE.WebGLRenderTarget( readBuffer.width, readBuffer.height, this.params );
+
+	}
+
+
+	if( this.accumulateIndex < jitterOffsets.length ) {
+		var autoClear = renderer.autoClear;
+		renderer.autoClear = false;
+
+		this.accumulateUniforms[ "scale" ].value = 1.0 / ( jitterOffsets.length );
+		this.accumulateUniforms[ "tForeground" ].value = writeBuffer;
+
+		// render the scene multiple times, each slightly jitter offset from the last and accumulate the results.
+		var numSamplesPerFrame = Math.pow( 2, this.sampleLevel );
+		for ( var i = 0; i < numSamplesPerFrame; i ++ ) {
+
+			var j = this.accumulateIndex;
+			// only jitters perspective cameras.	TODO: add support for jittering orthogonal cameras
+			var jitterOffset = jitterOffsets[j];
+			if ( camera.setViewOffset ) {
+				camera.setViewOffset( readBuffer.width, readBuffer.height,
+					jitterOffset[ 0 ] * 0.0625, jitterOffset[ 1 ] * 0.0625,   // 0.0625 = 1 / 16
+					readBuffer.width, readBuffer.height );
+			}
+
+			renderer.render( this.scene, this.camera, writeBuffer, true );
+
+			this.accumulateUniforms[ "scale" ].value = 1.0 / ( this.accumulateIndex + 1 );
+			renderer.render( this.scene3, this.camera3, this.sampleRenderTarget, ( this.accumulateIndex == 0 ) );
+
+			this.accumulateIndex ++;
+			if( this.accumulateIndex >= jitterOffsets.length ) break;
+		}
+
+		// reset jitter to nothing.	TODO: add support for orthogonal cameras
+		if ( camera.setViewOffset ) camera.setViewOffset( undefined, undefined, undefined, undefined, undefined, undefined );
+
+		renderer.autoClear = true;
+
+	}
+
+	this.accumulateUniforms[ "scale" ].value = 1.0;
+	this.accumulateUniforms[ "tForeground" ].value = this.sampleRenderTarget;
+	renderer.render( this.scene3, this.camera3, writeBuffer );
+
+}

+ 8 - 7
examples/webgl_postprocessing_msaa.html

@@ -29,6 +29,7 @@
 	<body>
 		<div id="info">
 			<a href="http://threejs.org" target="_blank">three.js</a> - Manual Multi-Sample Anti-Aliasing (MSAA) pass by <a href="https://clara.io" target="_blank">Ben Houston</a><br/><br/>
+			This manual approach to MSAA re-renders the scene with camera jitter and accumulates the results.  It is a slow approach.<br/><br/>
 			Texture interpolation, mipmapping and anistropic sampling is disabled to emphasize<br/> the effect MSAA levels have one the resulting render quality.
 		</div>
 
@@ -50,7 +51,7 @@
 
 		<script>
 
-			var camera, scene, renderer, composer, copyPass, manualMSAARenderPass;
+			var camera, scene, renderer, composer, copyPass, msaaRenderPass;
 			var gui, stats, texture;
 
 			var param = { MSAASampleLevel: 2 };
@@ -74,8 +75,8 @@
 					'Level 4: 16 Samples': 4
 				} ).onFinishChange( function() {
 
-					if( manualMSAARenderPass ) {
-						manualMSAARenderPass.sampleLevel = param.MSAASampleLevel;
+					if( msaaRenderPass ) {
+						msaaRenderPass.sampleLevel = param.MSAASampleLevel;
 					}
 
 				} );
@@ -128,9 +129,9 @@
 
 				composer = new THREE.EffectComposer( renderer );
 
-				manualMSAARenderPass = new THREE.ManualMSAARenderPass( scene, camera );
-				manualMSAARenderPass.sampleLevel = param.MSAASampleLevel;
-				composer.addPass( manualMSAARenderPass );
+				msaaRenderPass = new THREE.ManualMSAARenderPass( scene, camera );
+				msaaRenderPass.sampleLevel = param.MSAASampleLevel;
+				composer.addPass( msaaRenderPass );
 
 				copyPass = new THREE.ShaderPass( THREE.CopyShader );
 		    copyPass.renderToScreen = true;
@@ -154,7 +155,7 @@
 				var newWidth  = Math.floor( width / pixelRatio ) || 1;
 				var newHeight = Math.floor( height / pixelRatio ) || 1;
 				composer.setSize( newWidth, newHeight );
-				msaaPass.setSize( newWidth, newHeight );
+				msaaRenderPass.setSize( newWidth, newHeight );
 
 			}
 

+ 213 - 0
examples/webgl_postprocessing_taa.html

@@ -0,0 +1,213 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<title>three.js webgl - postprocessing manual msaa</title>
+		<meta charset="utf-8">
+		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
+		<style>
+			body {
+				margin: 0px;
+				background-color: #000;
+				overflow: hidden;
+				font-family:Monospace;
+				font-size:13px;
+				margin: 0px;
+				text-align:center;
+				overflow: hidden;
+			}
+
+			#info {
+				color: #fff;
+				position: absolute;
+				top: 10px;
+				width: 100%;
+				text-align: center;
+				display:block;
+			}
+		</style>
+	</head>
+	<body>
+		<div id="info">
+			<a href="http://threejs.org" target="_blank">three.js</a> - Temporal Anti-Aliasing (TAA) pass by <a href="https://clara.io" target="_blank">Ben Houston</a><br/><br/>
+			When the rotation stops the scene automatically improves its static visual quality by accumulating jittered renders.<br/>
+			This allows for the static quality of a high quality, but slow MSAA shader at the cost of a single render per frame (if sampleLevel = 0).<br/><br/>
+			Texture interpolation, mipmapping and anistropic sampling is disabled to emphasize<br/> the effect MSAA levels have one the resulting render quality.
+		</div>
+
+		<div id="container"></div>
+
+		<script src="../build/three.min.js"></script>
+		<script src="js/libs/stats.min.js"></script>
+		<script src="js/libs/dat.gui.min.js"></script>
+
+		<script src="js/postprocessing/ManualMSAARenderPass.js"></script>
+		<script src="js/postprocessing/TAARenderPass.js"></script>
+		<script src="js/shaders/CopyShader.js"></script>
+		<script src="js/shaders/CompositeShader.js"></script>
+
+		<script src="js/postprocessing/EffectComposer.js"></script>
+		<script src="js/postprocessing/RenderPass.js"></script>
+		<script src="js/postprocessing/MaskPass.js"></script>
+		<script src="js/postprocessing/ShaderPass.js"></script>
+
+
+		<script>
+
+			var camera, scene, renderer, composer, copyPass, taaRenderPass, renderPass;
+			var gui, stats, texture;
+
+			var param = { TAAEnabled: "1", TAASampleLevel: 0 };
+
+			init();
+			animate();
+
+			clearGui();
+
+			function clearGui() {
+
+				if ( gui ) gui.destroy();
+
+				gui = new dat.GUI();
+
+				gui.add( param, 'TAAEnabled', {
+					'Disabled': '0',
+					'Enabled': '1'
+				} ).onFinishChange( function() {
+
+					if( taaRenderPass ) {
+
+						taaRenderPass.enabled = ( param.TAAEnabled === "1" );
+						renderPass.enabled = ( param.TAAEnabled !== "1" );
+
+					}
+
+				} );
+
+				gui.add( param, 'TAASampleLevel', {
+					'Level 0: 1 Sample': 0,
+					'Level 1: 2 Samples': 1,
+					'Level 2: 4 Samples': 2,
+					'Level 3: 8 Samples': 3,
+					'Level 4: 16 Samples': 4
+				} ).onFinishChange( function() {
+
+					if( taaRenderPass ) {
+						taaRenderPass.sampleLevel = param.TAASampleLevel;
+					}
+
+				} );
+
+				gui.open();
+
+			}
+
+			function init() {
+
+				container = document.getElementById( "container" );
+
+				renderer = new THREE.WebGLRenderer( { antialias: false } );
+				renderer.setPixelRatio( window.devicePixelRatio );
+				renderer.setSize( window.innerWidth, window.innerHeight );
+				document.body.appendChild( renderer.domElement );
+
+				stats = new Stats();
+				stats.domElement.style.position = 'absolute';
+				stats.domElement.style.top = '0px';
+				container.appendChild( stats.domElement );
+
+				//
+
+				camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 1, 1000 );
+				camera.position.z = 300;
+
+				scene = new THREE.Scene();
+
+				var geometry = new THREE.BoxGeometry( 120, 120, 120 );
+				var material = new THREE.MeshBasicMaterial( { color: 0xffffff, wireframe: true } );
+
+				var mesh = new THREE.Mesh( geometry, material );
+				mesh.position.x = - 100;
+				scene.add( mesh );
+
+				var texture = new THREE.TextureLoader().load( "textures/brick_diffuse.jpg" );
+				texture.minFilter = THREE.NearestFilter;
+				texture.magFilter = THREE.NearestFilter;
+				texture.anisotropy = 1;
+				texture.generateMipmaps = false;
+
+				var material = new THREE.MeshBasicMaterial( { map: texture } );
+
+				var mesh = new THREE.Mesh( geometry, material );
+				mesh.position.x = 100;
+				scene.add( mesh );
+
+				// postprocessing
+
+				composer = new THREE.EffectComposer( renderer );
+
+				taaRenderPass = new THREE.TAARenderPass( scene, camera );
+				composer.addPass( taaRenderPass );
+
+				renderPass = new THREE.RenderPass( scene, camera );
+				renderPass.enabled = false;
+				composer.addPass( renderPass );
+
+				copyPass = new THREE.ShaderPass( THREE.CopyShader );
+		    copyPass.renderToScreen = true;
+				composer.addPass( copyPass );
+
+				window.addEventListener( 'resize', onWindowResize, false );
+
+			}
+
+			function onWindowResize() {
+
+				var width = window.innerWidth;
+				var height = window.innerHeight;
+
+				camera.aspect = width / height;
+				camera.updateProjectionMatrix();
+
+				renderer.setSize( width, height );
+
+				var pixelRatio = renderer.getPixelRatio();
+				var newWidth  = Math.floor( width / pixelRatio ) || 1;
+				var newHeight = Math.floor( height / pixelRatio ) || 1;
+				composer.setSize( newWidth, newHeight );
+				taaRenderPass.setSize( newWidth, newHeight );
+
+			}
+
+			function animate() {
+
+				this.index = this.index || 0;
+
+				requestAnimationFrame( animate );
+
+				this.index ++;
+
+				if( Math.round( this.index / 200 ) % 2 === 0 ) {
+					for ( var i = 0; i < scene.children.length; i ++ ) {
+
+						var child = scene.children[ i ];
+
+						child.rotation.x += 0.005;
+						child.rotation.y += 0.01;
+
+					}
+					if( taaRenderPass ) taaRenderPass.accumulate = false;
+				}
+				else {
+					if( taaRenderPass ) taaRenderPass.accumulate = true;
+				}
+
+				composer.render();
+
+				stats.update();
+
+			}
+
+		</script>
+		<div>
+	</body>
+</html>