/** * @author zz85 https://github.com/zz85 / http://www.lab4games.net/zz85/blog * * Bird Simulation Render * * A simple scene rendering a quad of the following shaders * 1. Pass-thru Shader, * 2. Bird Position Update Shader, * 3. Bird Velocity Update Shader * */ function SimulationRenderer( WIDTH, renderer ) { WIDTH = WIDTH || 4; var camera = new THREE.Camera(); camera.position.z = 1; // Init RTT stuff gl = renderer.getContext(); if ( ! gl.getExtension( "OES_texture_float" ) ) { alert( "No OES_texture_float support for float textures!" ); return; } if ( gl.getParameter( gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS ) == 0 ) { alert( "No support for vertex shader textures!" ); return; } var scene = new THREE.Scene(); var uniforms = { time: { type: "f", value: 1.0 }, resolution: { type: "v2", value: new THREE.Vector2( WIDTH, WIDTH ) }, texture: { type: "t", value: null } }; var passThruShader = new THREE.ShaderMaterial( { uniforms: uniforms, vertexShader: document.getElementById( 'vertexShader' ).textContent, fragmentShader: document.getElementById( 'fragmentShader' ).textContent } ); var mesh = new THREE.Mesh( new THREE.PlaneBufferGeometry( 2, 2 ), passThruShader ); var positionShader = new THREE.ShaderMaterial( { uniforms: { time: { type: "f", value: 1.0 }, delta: { type: "f", value: 0.0 }, resolution: { type: "v2", value: new THREE.Vector2( WIDTH, WIDTH ) }, texturePosition: { type: "t", value: null }, textureVelocity: { type: "t", value: null }, }, vertexShader: document.getElementById( 'vertexShader' ).textContent, fragmentShader: document.getElementById( 'fragmentShaderPosition' ).textContent } ); this.positionShader = positionShader; var velocityShader = new THREE.ShaderMaterial( { uniforms: { time: { type: "f", value: 1.0 }, delta: { type: "f", value: 0.0 }, resolution: { type: "v2", value: new THREE.Vector2( WIDTH, WIDTH ) }, texturePosition: { type: "t", value: null }, textureVelocity: { type: "t", value: null }, testing: { type: "f", value: 1.0 }, seperationDistance: { type: "f", value: 1.0 }, alignmentDistance: { type: "f", value: 1.0 }, cohesionDistance: { type: "f", value: 1.0 }, freedomFactor: { type: "f", value: 1.0 }, predator: { type: "v3", value: new THREE.Vector3() } }, defines: { WIDTH: WIDTH.toFixed( 2 ) }, vertexShader: document.getElementById( 'vertexShader' ).textContent, fragmentShader: document.getElementById( 'fragmentShaderVelocity' ).textContent } ); this.velocityUniforms = velocityShader.uniforms; scene.add( mesh ); var flipflop = true; var rtPosition1, rtPosition2, rtVelocity1, rtVelocity2; function init() { var dtPosition = generatePositionTexture(); var dtVelocity = generateVelocityTexture(); rtPosition1 = getRenderTarget( THREE.RGBAFormat ); rtPosition2 = rtPosition1.clone(); rtVelocity1 = getRenderTarget( THREE.RGBFormat ); rtVelocity2 = rtVelocity1.clone(); simulator.renderTexture( dtPosition, rtPosition1 ); simulator.renderTexture( rtPosition1, rtPosition2 ); simulator.renderTexture( dtVelocity, rtVelocity1 ); simulator.renderTexture( rtVelocity1, rtVelocity2 ); simulator.velocityUniforms.testing.value = 10; } this.init = init; function getRenderTarget( type ) { var renderTarget = new THREE.WebGLRenderTarget( WIDTH, WIDTH, { wrapS: THREE.RepeatWrapping, wrapT: THREE.RepeatWrapping, minFilter: THREE.NearestFilter, magFilter: THREE.NearestFilter, format: type, type: THREE.FloatType, stencilBuffer: false } ); return renderTarget; } // Takes a texture, and render out as another texture this.renderTexture = function ( input, output ) { mesh.material = passThruShader; uniforms.texture.value = input; renderer.render( scene, camera, output ); }; this.renderPosition = function( position, velocity, output, delta ) { mesh.material = positionShader; positionShader.uniforms.texturePosition.value = position; positionShader.uniforms.textureVelocity.value = velocity; positionShader.uniforms.time.value = performance.now(); positionShader.uniforms.delta.value = delta; renderer.render( scene, camera, output ); this.currentPosition = output; }; this.renderVelocity = function( position, velocity, output, delta ) { mesh.material = velocityShader; velocityShader.uniforms.texturePosition.value = position; velocityShader.uniforms.textureVelocity.value = velocity; velocityShader.uniforms.time.value = performance.now(); velocityShader.uniforms.delta.value = delta; renderer.render( scene, camera, output ); this.currentVelocity = output; }; this.simulate = function( delta ) { if ( flipflop ) { simulator.renderVelocity( rtPosition1, rtVelocity1, rtVelocity2, delta ); simulator.renderPosition( rtPosition1, rtVelocity2, rtPosition2, delta ); } else { simulator.renderVelocity( rtPosition2, rtVelocity2, rtVelocity1, delta ); simulator.renderPosition( rtPosition2, rtVelocity1, rtPosition1, delta ); } flipflop = ! flipflop; }; function generatePositionTexture() { var a = new Float32Array( PARTICLES * 4 ); for ( var k = 0, kl = a.length; k < kl; k += 4 ) { var x = Math.random() * BOUNDS - BOUNDS_HALF; var y = Math.random() * BOUNDS - BOUNDS_HALF; var z = Math.random() * BOUNDS - BOUNDS_HALF; a[ k + 0 ] = x; a[ k + 1 ] = y; a[ k + 2 ] = z; a[ k + 3 ] = 1; } var texture = new THREE.DataTexture( a, WIDTH, WIDTH, THREE.RGBAFormat, THREE.FloatType ); texture.needsUpdate = true; return texture; } function generateVelocityTexture() { var a = new Float32Array( PARTICLES * 3 ); for ( var k = 0, kl = a.length; k < kl; k += 3 ) { var x = Math.random() - 0.5; var y = Math.random() - 0.5; var z = Math.random() - 0.5; a[ k + 0 ] = x * 10; a[ k + 1 ] = y * 10; a[ k + 2 ] = z * 10; } var texture = new THREE.DataTexture( a, WIDTH, WIDTH, THREE.RGBFormat, THREE.FloatType ); texture.needsUpdate = true; return texture; } }