浏览代码

intro NodePostProcessing

sunag 7 年之前
父节点
当前提交
740fcd890e

+ 1 - 0
examples/files.js

@@ -234,6 +234,7 @@ var files = {
 		"webgl_postprocessing_ssaa",
 		"webgl_postprocessing_ssaa",
 		"webgl_postprocessing_ssaa_unbiased",
 		"webgl_postprocessing_ssaa_unbiased",
 		"webgl_postprocessing_nodes",
 		"webgl_postprocessing_nodes",
+		"webgl_postprocessing_nodes_pass",
 		"webgl_postprocessing_outline",
 		"webgl_postprocessing_outline",
 		"webgl_postprocessing_pixel",
 		"webgl_postprocessing_pixel",
 		"webgl_postprocessing_procedural",
 		"webgl_postprocessing_procedural",

+ 1 - 0
examples/js/nodes/Nodes.js

@@ -108,4 +108,5 @@ export { MeshStandardNodeMaterial } from './materials/MeshStandardNodeMaterial.j
 
 
 // postprocessing
 // postprocessing
 
 
+export { NodePostProcessing } from './postprocessing/NodePostProcessing.js';
 //export { NodePass } from './postprocessing/NodePass.js';
 //export { NodePass } from './postprocessing/NodePass.js';

+ 9 - 1
examples/js/nodes/THREE.Nodes.js

@@ -104,7 +104,11 @@ import {
 	SpriteNodeMaterial,
 	SpriteNodeMaterial,
 	PhongNodeMaterial,
 	PhongNodeMaterial,
 	StandardNodeMaterial,
 	StandardNodeMaterial,
-	MeshStandardNodeMaterial
+	MeshStandardNodeMaterial,
+	
+	// post-processing
+	
+	NodePostProcessing
 	
 	
 } from './Nodes.js';
 } from './Nodes.js';
 
 
@@ -213,3 +217,7 @@ THREE.SpriteNodeMaterial = SpriteNodeMaterial;
 THREE.PhongNodeMaterial = PhongNodeMaterial;
 THREE.PhongNodeMaterial = PhongNodeMaterial;
 THREE.StandardNodeMaterial = StandardNodeMaterial;
 THREE.StandardNodeMaterial = StandardNodeMaterial;
 THREE.MeshStandardNodeMaterial = MeshStandardNodeMaterial;
 THREE.MeshStandardNodeMaterial = MeshStandardNodeMaterial;
+
+// post-processing
+
+THREE.NodePostProcessing = NodePostProcessing;

+ 13 - 5
examples/js/nodes/accessors/ResolutionNode.js

@@ -16,11 +16,19 @@ ResolutionNode.prototype.nodeType = "Resolution";
 
 
 ResolutionNode.prototype.updateFrame = function ( frame ) {
 ResolutionNode.prototype.updateFrame = function ( frame ) {
 
 
-	var size = frame.renderer.getSize(),
-		pixelRatio = frame.renderer.getPixelRatio();
-
-	this.x = size.width * pixelRatio;
-	this.y = size.height * pixelRatio;
+	if ( frame.renderer ) {
+		
+		var size = frame.renderer.getSize(),
+			pixelRatio = frame.renderer.getPixelRatio();
+
+		this.x = size.width * pixelRatio;
+		this.y = size.height * pixelRatio;
+		
+	} else {
+		
+		console.warn("ResolutionNode need a renderer in NodeFrame");
+		
+	}
 
 
 };
 };
 
 

+ 8 - 0
examples/js/nodes/core/NodeFrame.js

@@ -33,6 +33,14 @@ NodeFrame.prototype = {
 		
 		
 	},
 	},
 	
 	
+	setRenderTexture: function( renderTexture ) {
+		
+		this.renderTexture = renderTexture;
+		
+		return this;
+		
+	},
+	
 	updateNode: function ( node ) {
 	updateNode: function ( node ) {
 
 
 		if ( node.frameId === this.id ) return this;
 		if ( node.frameId === this.id ) return this;

+ 47 - 25
examples/js/nodes/inputs/RTTNode.js

@@ -43,52 +43,74 @@ RTTNode.prototype.build = function ( builder, output, uuid ) {
 	
 	
 	this.material.fragment.value = this.input;
 	this.material.fragment.value = this.input;
 	this.material.build( { builder: rttBuilder } );
 	this.material.build( { builder: rttBuilder } );
-	
+
 	return TextureNode.prototype.build.call( this, builder, output, uuid );
 	return TextureNode.prototype.build.call( this, builder, output, uuid );
 };
 };
 
 
+RTTNode.prototype.updateFrameSaveToRTT = function ( frame ) {
+	
+	this.saveToRTT.render = false;
+			
+	if (this.saveToRTT !== this.saveToRTTCurrent) {
+		
+		if (this.saveToRTTMaterial) this.saveToRTTMaterial.dispose();
+		
+		var material = new THREE.NodeMaterial();
+		material.fragment.value = this;
+		material.build();
+		
+		var scene = new THREE.Scene();
+		
+		var quad = new THREE.Mesh( new THREE.PlaneBufferGeometry( 2, 2 ), material );
+		quad.frustumCulled = false; // Avoid getting clipped
+		scene.add( quad );
+		
+		this.saveToRTTScene = scene;
+		this.saveToRTTMaterial = material;
+		
+	}
+	
+	this.saveToRTTCurrent = this.saveToRTT;
+
+	frame.renderer.render( this.saveToRTTScene, this.camera, this.saveToRTT.renderTarget, this.saveToRTT.clear );
+	
+};
+
 RTTNode.prototype.updateFrame = function ( frame ) {
 RTTNode.prototype.updateFrame = function ( frame ) {
 	
 	
 	if ( frame.renderer ) {
 	if ( frame.renderer ) {
 		
 		
-		if (this.render) {
+		// from the second frame
+		
+		if (this.saveToRTT && this.saveToRTT.render === false) {
 			
 			
-			frame.renderer.render( this.scene, this.camera, this.renderTarget, this.clear );
+			this.updateFrameSaveToRTT( frame );
 			
 			
 		}
 		}
 		
 		
-		if (this.saveToRTT) {
+		if (this.render) {
 			
 			
-			this.saveToRTT.render = false;
+			if (this.material.uniforms.renderTexture) {
 			
 			
-			if (this.saveToRTT !== this.saveToRTTCurrent) {
-				
-				if (this.saveToRTTMaterial) this.saveToRTTMaterial.dispose();
-				
-				var material = new THREE.NodeMaterial();
-				material.fragment.value = this;
-				material.build();
-				
-				var scene = new THREE.Scene();
-				
-				var quad = new THREE.Mesh( new THREE.PlaneBufferGeometry( 2, 2 ), material );
-				quad.frustumCulled = false; // Avoid getting clipped
-				scene.add( quad );
-				
-				this.saveToRTTScene = scene;
-				this.saveToRTTMaterial = material;
+				this.material.uniforms.renderTexture.value = frame.renderTexture;
 				
 				
 			}
 			}
 			
 			
-			this.saveToRTTCurrent = this.saveToRTT;
-
-			frame.renderer.render( this.saveToRTTScene, this.camera, this.saveToRTT.renderTarget, this.saveToRTT.clear );
+			frame.renderer.render( this.scene, this.camera, this.renderTarget, this.clear );
+			
+		}
+		
+		// first frame
+		
+		if (this.saveToRTT && this.saveToRTT.render === true) {
+			
+			this.updateFrameSaveToRTT( frame );
 			
 			
 		}
 		}
 		
 		
 	} else {
 	} else {
 		
 		
-		console.warn("RTTNode need a renderer in NodeFrame")
+		console.warn("RTTNode need a renderer in NodeFrame");
 		
 		
 	}
 	}
 	
 	

+ 128 - 0
examples/js/nodes/postprocessing/NodePostProcessing.js

@@ -0,0 +1,128 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+import { NodeMaterial } from '../materials/NodeMaterial.js';
+import { ScreenNode } from '../inputs/ScreenNode.js';
+
+function NodePostProcessing( renderer, renderTarget ) {
+
+	if ( renderTarget === undefined ) {
+
+		var parameters = {
+			minFilter: THREE.LinearFilter,
+			magFilter: THREE.LinearFilter,
+			format: THREE.RGBAFormat,
+			stencilBuffer: false
+		};
+
+		var size = renderer.getDrawingBufferSize();
+		renderTarget = new THREE.WebGLRenderTarget( size.width, size.height, parameters );
+
+	}
+	
+	this.renderer = renderer;
+	this.renderTarget = renderTarget;
+
+	this.input = new ScreenNode();
+	this.material = new NodeMaterial();
+
+	this.camera = new THREE.OrthographicCamera( - 1, 1, 1, - 1, 0, 1 );
+	this.scene = new THREE.Scene();
+
+	this.quad = new THREE.Mesh( new THREE.PlaneBufferGeometry( 2, 2 ), this.material );
+	this.quad.frustumCulled = false; // Avoid getting clipped
+	this.scene.add( this.quad );
+	
+	this.needsUpdate = true;
+
+};
+
+NodePostProcessing.prototype = {
+
+	constructor: NodePostProcessing,
+
+	render: function ( scene, camera, frame ) {
+		
+		if ( this.needsUpdate ) {
+
+			this.material.dispose();
+
+			this.material.fragment.value = this.input;
+			this.material.build();
+
+			if (this.material.uniforms.renderTexture) {
+				
+				this.material.uniforms.renderTexture.value = this.renderTarget.texture;
+				
+			}
+
+			this.needsUpdate = false;
+
+		}
+
+		frame.setRenderer( this.renderer )
+			.setRenderTexture( this.renderTarget.texture );
+		
+		this.renderer.render( scene, camera, this.renderTarget );
+
+		frame.updateNode( this.material );
+		
+		this.renderer.render( this.scene, this.camera );
+		
+	},
+	
+	setSize: function ( width, height ) {
+		
+		this.renderTarget.setSize( width, height );
+	
+		this.renderer.setSize( width, height );
+		
+	},
+	
+	copy: function ( source ) {
+		
+		this.material = source.material;
+		
+	},
+	
+	toJSON: function ( meta ) {
+		
+		var isRootObject = ( meta === undefined || typeof meta === 'string' );
+
+		if ( isRootObject ) {
+
+			meta = {
+				nodes: {}
+			};
+
+		}
+
+		if ( meta && ! meta.postprocessing ) meta.postprocessing = {};
+
+		if ( ! meta.postprocessing[ this.uuid ] ) {
+
+			var data = {};
+
+			data.uuid = this.uuid;
+			data.type = "NodePostProcessing";
+
+			meta.postprocessing[ this.uuid ] = data;
+
+			if ( this.name !== "" ) data.name = this.name;
+
+			if ( JSON.stringify( this.userData ) !== '{}' ) data.userData = this.userData;
+
+			data.material = this.material.toJSON( meta ).uuid;
+
+		}
+
+		meta.postprocessing = this.uuid;
+
+		return meta;
+		
+	}
+	
+};
+
+export { NodePostProcessing };

+ 572 - 0
examples/webgl_postprocessing_nodes.html

@@ -0,0 +1,572 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<title>three.js webgl - postprocessing with nodes</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" rel="noopener">three.js</a> - Node-Based Post-Processing
+		</div>
+
+		<script src="../build/three.js"></script>
+		<script src="js/libs/dat.gui.min.js"></script>
+
+		<script type="module">
+
+			import './js/nodes/THREE.Nodes.js';
+			import { NodePostProcessing } from './js/nodes/postprocessing/NodePostProcessing.js';
+			import './js/loaders/NodeMaterialLoader.js';
+
+			var camera, scene, renderer;
+			var object, light, nodepost;
+			var gui, guiElements = [];
+
+			var clock = new THREE.Clock();
+			var frame = new THREE.NodeFrame();
+
+			var param = { example: new URL(window.location.href).searchParams.get('e') || 'color-adjustment' };
+
+			var textureLoader = new THREE.TextureLoader();
+
+			var lensflare2 = textureLoader.load( 'textures/lensflare/lensflare0.png' );
+			lensflare2.wrapS = lensflare2.wrapT = THREE.RepeatWrapping;
+
+			var decalNormal = textureLoader.load( 'textures/decal/decal-normal.jpg' );
+			decalNormal.wrapS = decalNormal.wrapT = THREE.RepeatWrapping;
+
+			init();
+			animate();
+
+			function clearGui() {
+
+				if ( gui ) gui.destroy();
+
+				gui = new dat.GUI();
+
+				var example = gui.add( param, 'example', {
+					'basic / color-adjustment': 'color-adjustment',
+					'basic / blends': 'blends',
+					'basic / fade': 'fade',
+					'basic / invert': 'invert',
+					'basic / blur': 'blur',
+					'adv / motion-blur': 'motion-blur',
+					'adv / saturation': 'saturation',
+					'adv / refraction': 'refraction',
+					'adv / mosaic': 'mosaic'
+				} ).onFinishChange( function () {
+
+					updateMaterial();
+
+				} );
+
+				gui.open();
+
+			}
+
+			function addGui( name, value, callback, isColor, min, max ) {
+
+				var node;
+
+				param[ name ] = value;
+
+				if ( isColor ) {
+
+					node = gui.addColor( param, name ).onChange( function () {
+
+						callback( param[ name ] );
+
+					} );
+
+				} else if ( typeof value == 'object' ) {
+
+					param[ name ] = value[ Object.keys( value )[ 0 ] ];
+
+					node = gui.add( param, name, value ).onChange( function () {
+
+						callback( param[ name ] );
+
+					} );
+
+				} else {
+
+					node = gui.add( param, name, min, max ).onChange( function () {
+
+						callback( param[ name ] );
+
+					} );
+
+				}
+
+				return node;
+
+			}
+
+			function updateMaterial() {
+
+				var name = param.example;
+
+				clearGui();
+
+				switch ( name ) {
+
+					case 'color-adjustment':
+
+						// POST
+					
+						var screen = new THREE.ScreenNode();
+
+						var hue = new THREE.FloatNode();
+						var sataturation = new THREE.FloatNode( 1 );
+						var vibrance = new THREE.FloatNode();
+						var brightness = new THREE.FloatNode( 0 );
+						var contrast = new THREE.FloatNode( 1 );
+
+						var hueNode = new THREE.ColorAdjustmentNode( screen, hue, THREE.ColorAdjustmentNode.HUE );
+						var satNode = new THREE.ColorAdjustmentNode( hueNode, sataturation, THREE.ColorAdjustmentNode.SATURATION );
+						var vibranceNode = new THREE.ColorAdjustmentNode( satNode, vibrance, THREE.ColorAdjustmentNode.VIBRANCE );
+						var brightnessNode = new THREE.ColorAdjustmentNode( vibranceNode, brightness, THREE.ColorAdjustmentNode.BRIGHTNESS );
+						var contrastNode = new THREE.ColorAdjustmentNode( brightnessNode, contrast, THREE.ColorAdjustmentNode.CONTRAST );
+
+						nodepost.input = contrastNode;
+
+						// GUI
+
+						addGui( 'hue', hue.value, function ( val ) {
+
+							hue.value = val;
+
+						}, false, 0, Math.PI * 2 );
+
+						addGui( 'saturation', sataturation.value, function ( val ) {
+
+							sataturation.value = val;
+
+						}, false, 0, 2 );
+
+						addGui( 'vibrance', vibrance.value, function ( val ) {
+
+							vibrance.value = val;
+
+						}, false, - 1, 1 );
+
+						addGui( 'brightness', brightness.value, function ( val ) {
+
+							brightness.value = val;
+
+						}, false, 0, .5 );
+
+						addGui( 'contrast', contrast.value, function ( val ) {
+
+							contrast.value = val;
+
+						}, false, 0, 2 );
+
+						break;
+
+					case 'fade':
+
+						// POST
+
+						var color = new THREE.ColorNode( 0xFFFFFF );
+						var percent = new THREE.FloatNode( .5 );
+
+						var fade = new THREE.Math3Node(
+							new THREE.ScreenNode(),
+							color,
+							percent,
+							THREE.Math3Node.MIX
+						);
+
+						nodepost.input = fade;
+
+						// GUI
+
+						addGui( 'color', color.value.getHex(), function ( val ) {
+
+							color.value.setHex( val );
+
+						}, true );
+
+						addGui( 'fade', percent.value, function ( val ) {
+
+							percent.value = val;
+
+						}, false, 0, 1 );
+
+						break;
+
+					case 'invert':
+
+						// POST
+
+						var alpha = new THREE.FloatNode( 1 );
+
+						var screen = new THREE.ScreenNode();
+						var inverted = new THREE.Math1Node( screen, THREE.Math1Node.INVERT );
+
+						var fade = new THREE.Math3Node(
+							screen,
+							inverted,
+							alpha,
+							THREE.Math3Node.MIX
+						);
+
+						nodepost.input = fade;
+
+						// GUI
+
+						addGui( 'alpha', alpha.value, function ( val ) {
+
+							alpha.value = val;
+
+						}, false, 0, 1 );
+
+						break;
+
+					case 'blends':
+
+						// POST
+
+						var multiply = new THREE.OperatorNode(
+							new THREE.ScreenNode(),
+							new THREE.TextureNode( lensflare2 ),
+							THREE.OperatorNode.ADD
+						);
+
+						nodepost.input = multiply;
+
+						// GUI
+
+						addGui( 'blend', {
+							'addition': THREE.OperatorNode.ADD,
+							'subtract': THREE.OperatorNode.SUB,
+							'multiply': THREE.OperatorNode.MUL,
+							'division': THREE.OperatorNode.DIV
+						}, function ( val ) {
+
+							multiply.op = val;
+
+							nodepost.needsUpdate = true;
+
+						} );
+
+						break;
+
+					case 'saturation':
+
+						// PASS
+
+						var screen = new THREE.ScreenNode();
+						var sat = new THREE.FloatNode( 0 );
+
+						var satrgb = new THREE.FunctionNode( [
+							"vec3 satrgb( vec3 rgb, float adjustment ) {",
+							// include luminance function from LuminanceNode
+							"	vec3 intensity = vec3( luminance( rgb ) );",
+							"	return mix( intensity, rgb, adjustment );",
+							"}"
+						].join( "\n" ), [ THREE.LuminanceNode.Nodes.luminance ] );
+
+						var saturation = new THREE.FunctionCallNode( satrgb );
+						saturation.inputs.rgb = screen;
+						saturation.inputs.adjustment = sat;
+
+						nodepost.input = saturation;
+
+						// GUI
+
+						addGui( 'saturation', sat.value, function ( val ) {
+
+							sat.value = val;
+
+						}, false, 0, 2 );
+
+						break;
+
+					case 'refraction':
+
+						// POST
+
+						var normal = new THREE.TextureNode( decalNormal );
+						var normalXY = new THREE.SwitchNode( normal, 'xy' );
+						var scale = new THREE.FloatNode( .5 );
+						var flip = new THREE.Vector2Node( - 1, 1 );
+
+						var normalXYFlip = new THREE.Math1Node(
+							normalXY,
+							THREE.Math1Node.INVERT
+						);
+
+						var offsetNormal = new THREE.OperatorNode(
+							normalXYFlip,
+							new THREE.FloatNode( .5 ),
+							THREE.OperatorNode.ADD
+						);
+
+						var scaleTexture = new THREE.OperatorNode(
+							new THREE.SwitchNode( normal, 'z' ),
+							offsetNormal,
+							THREE.OperatorNode.MUL
+						);
+
+						var scaleNormal = new THREE.Math3Node(
+							new THREE.FloatNode( 1 ),
+							scaleTexture,
+							scale,
+							THREE.Math3Node.MIX
+						);
+
+						var offsetCoord = new THREE.OperatorNode(
+							new THREE.UVNode(),
+							scaleNormal,
+							THREE.OperatorNode.MUL
+						);
+
+						var screen = new THREE.ScreenNode( offsetCoord );
+
+						nodepost.input = screen;
+
+						// GUI
+
+						addGui( 'scale', scale.value, function ( val ) {
+
+							scale.value = val;
+
+						}, false, 0, 1 );
+
+						addGui( 'invert', false, function ( val ) {
+
+							offsetNormal.a = val ? normalXYFlip : normalXY;
+
+							nodepost.needsUpdate = true;
+
+						} );
+
+						break;
+
+					case 'motion-blur':
+
+						// POST
+					
+						var size = renderer.getDrawingBufferSize();
+						
+						var screen = new THREE.ScreenNode();
+						
+						var previousFrame = new THREE.RTTNode( size.width, size.height, screen );
+						var previous2Frame = new THREE.RTTNode( size.width, size.height, screen );
+						previous2Frame.saveToRTT = previousFrame;
+						
+						var motionBlur = new THREE.Math3Node(
+							previousFrame,
+							previous2Frame,
+							new THREE.FloatNode(.5),
+							THREE.Math3Node.MIX
+						);
+						
+						var currentFrame = new THREE.RTTNode( size.width, size.height, motionBlur );
+						currentFrame.saveToRTT = previousFrame;
+						
+						nodepost.input = currentFrame;
+						
+						break;
+						
+					case 'mosaic':
+
+						// POST
+
+						var scale = new THREE.FloatNode( 128 );
+						var fade = new THREE.FloatNode( 1 );
+						var uv = new THREE.UVNode();
+
+						var blocks = new THREE.OperatorNode(
+							uv,
+							scale,
+							THREE.OperatorNode.MUL
+						);
+
+						var blocksSize = new THREE.Math1Node(
+							blocks,
+							THREE.Math1Node.FLOOR
+						);
+
+						var mosaicUV = new THREE.OperatorNode(
+							blocksSize,
+							scale,
+							THREE.OperatorNode.DIV
+						);
+
+						var fadeScreen = new THREE.Math3Node(
+							uv,
+							mosaicUV,
+							fade,
+							THREE.Math3Node.MIX
+						);
+
+						nodepost.input = new THREE.ScreenNode( fadeScreen );
+
+						// GUI
+
+						addGui( 'scale', scale.value, function ( val ) {
+
+							scale.value = val;
+
+						}, false, 16, 1024 );
+
+						addGui( 'fade', fade.value, function ( val ) {
+
+							fade.value = val;
+
+						}, false, 0, 1 );
+
+						addGui( 'mask', false, function ( val ) {
+
+							fadeScreen.c = val ? new THREE.TextureNode( lensflare2 ) : fade;
+
+							nodepost.needsUpdate = true;
+
+						} );
+
+						break;
+
+					case 'blur':
+
+						// POST
+
+						var size = renderer.getDrawingBufferSize();
+
+						var blurScreen = new THREE.BlurNode( new THREE.ScreenNode() );
+						blurScreen.size = new THREE.Vector2( size.width, size.height );
+
+						nodepost.input = blurScreen;
+
+						// GUI
+
+						addGui( 'blurX', blurScreen.radius.x, function ( val ) {
+
+							blurScreen.radius.x = val;
+
+						}, false, 0, 15 );
+
+						addGui( 'blurY', blurScreen.radius.y, function ( val ) {
+
+							blurScreen.radius.y = val;
+
+						}, false, 0, 15 );
+
+						break;
+
+				}
+
+				nodepost.needsUpdate = true;
+
+				// test serialization
+/*
+				var library = {};
+				library[ lensflare2.uuid ] = lensflare2;
+				library[ decalNormal.uuid ] = decalNormal;
+
+				var json = nodepost.toJSON();
+
+				nodepost.input = new THREE.NodeMaterialLoader( null, library ).parse( json ).value;
+*/
+			}
+
+			function init() {
+
+				renderer = new THREE.WebGLRenderer();
+				renderer.setPixelRatio( window.devicePixelRatio );
+				renderer.setSize( window.innerWidth, window.innerHeight );
+				document.body.appendChild( renderer.domElement );
+
+				nodepost = new NodePostProcessing( renderer );
+
+				//
+
+				camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 1, 1000 );
+				camera.position.z = 400;
+
+				scene = new THREE.Scene();
+				scene.fog = new THREE.Fog( 0x0066FF, 1, 1000 );
+
+				object = new THREE.Object3D();
+				scene.add( object );
+
+				var geometry = new THREE.SphereBufferGeometry( 1, 4, 4 );
+
+				for ( var i = 0; i < 100; i ++ ) {
+
+					var material = new THREE.MeshPhongMaterial( { color: 0x888888 + ( Math.random() * 0x888888 ), flatShading: true } );
+					var mesh = new THREE.Mesh( geometry, material );
+					mesh.position.set( Math.random() - 0.5, Math.random() - 0.5, Math.random() - 0.5 ).normalize();
+					mesh.position.multiplyScalar( Math.random() * 400 );
+					mesh.rotation.set( Math.random() * 2, Math.random() * 2, Math.random() * 2 );
+					mesh.scale.x = mesh.scale.y = mesh.scale.z = 10 + ( Math.random() * 40 );
+					object.add( mesh );
+
+				}
+
+				scene.add( new THREE.AmbientLight( 0x999999 ) );
+
+				light = new THREE.DirectionalLight( 0xffffff );
+				light.position.set( 1, 1, 1 );
+				scene.add( light );
+
+				//
+
+				updateMaterial();
+
+				window.addEventListener( 'resize', onWindowResize, false );
+
+			}
+
+			function onWindowResize() {
+
+				camera.aspect = window.innerWidth / window.innerHeight;
+				camera.updateProjectionMatrix();
+
+				nodepost.setSize( window.innerWidth, window.innerHeight );
+
+			}
+
+			function animate() {
+
+				requestAnimationFrame( animate );
+
+				var delta = clock.getDelta();
+
+				object.rotation.x += 0.005;
+				object.rotation.y += 0.01;
+
+				frame.update( delta );
+				
+				nodepost.render( scene, camera, frame );
+
+			}
+
+		</script>
+
+	</body>
+</html>