vanta
Version:
3D animated backgrounds for your website
862 lines (732 loc) • 26.9 kB
JavaScript
// Adapted from https://threejs.org/examples/canvas_geometry_birds.html
import VantaBase, { VANTA } from './_base.js'
// import {rn, ri, sample} from './helpers.js'
import {mobileCheck} from './helpers.js'
import GPUComputationRenderer from '../vendor/GPUComputationRenderer.js'
const win = typeof window == 'object'
let THREE = win && window.THREE
const GPGPU = !mobileCheck()
let WIDTH = 32
let BIRDS = WIDTH * WIDTH
const BOUNDS = 800
const BOUNDS_HALF = BOUNDS / 2
let Bird, Boid
const ATTACH_CLASSES = function(THREE) {
// Non-GPGPU geometry
Bird = function (options={}) {
var scope = this
THREE.Geometry.call( this )
v( 5, 0, 0 )
v( - 5, - 1, 1 )
v( - 5, 0, 0 )
v( - 5, - 2, - 1 )
v( 0, 2, - 6 )
v( 0, 2, 6 )
v( 2, 0, 0 )
v( - 3, 0, 0 )
f3( 0, 2, 1 )
// f3( 0, 3, 2 )
f3( 4, 7, 6 )
f3( 5, 6, 7 )
// this.computeCentroids()
this.computeFaceNormals()
function v( x, y, z ) {
const s = 1.5 * (options.birdSize || 1)
scope.vertices.push( new THREE.Vector3( x*s, y*s, z*s ) )
}
function f3( a, b, c ) {
scope.faces.push( new THREE.Face3( a, b, c ) )
}
}
Bird.prototype = Object.create( THREE.Geometry.prototype )
// Based on http://www.openprocessing.org/visuals/?visualID=6910
Boid = function(options) {
var vector = new THREE.Vector3(),
_acceleration,
_width = 500,
_height = 500,
_depth = 200, _goal,
_neighborhoodRadius = 100,
_maxSpeed = 2.5,
_maxSteerForce = 0.1,
_avoidWalls = true;
var _options = options
this.position = new THREE.Vector3()
this.velocity = new THREE.Vector3()
_acceleration = new THREE.Vector3()
this.setGoal = function ( target ) {
_goal = target;
}
// this.setAvoidWalls = function ( value ) {
// _avoidWalls = value;
// }
this.setWorldSize = function ( width, height, depth ) {
_width = width;
_height = height;vector
_depth = depth;
}
this.run = function ( boids ) {
if ( _avoidWalls ) {
vector.set( - _width, this.position.y, this.position.z )
vector = this.avoid( vector )
vector.multiplyScalar( 5 )
_acceleration.add( vector )
vector.set( _width, this.position.y, this.position.z )
vector = this.avoid( vector )
vector.multiplyScalar( 5 )
_acceleration.add( vector )
vector.set( this.position.x, - _height, this.position.z )
vector = this.avoid( vector )
vector.multiplyScalar( 5 )
_acceleration.add( vector )
vector.set( this.position.x, _height, this.position.z )
vector = this.avoid( vector )
vector.multiplyScalar( 5 )
_acceleration.add( vector )
vector.set( this.position.x, this.position.y, - _depth )
vector = this.avoid( vector )
vector.multiplyScalar( 5 )
_acceleration.add( vector )
vector.set( this.position.x, this.position.y, _depth )
vector = this.avoid( vector )
vector.multiplyScalar( 5 )
_acceleration.add( vector )
}/* else {
this.checkBounds();
}*/
if ( Math.random() > 0.5 ) {
this.flock( boids )
}
this.move()
}
this.flock = function ( boids ) {
if ( _goal ) {
_acceleration.add( this.reach( _goal, 0.005 ) )
}
_acceleration.add( this.alignment( boids ) )
_acceleration.add( this.cohesion( boids ) )
_acceleration.add( this.separation( boids ) )
}
this.move = function () {
this.velocity.add( _acceleration )
var l = this.velocity.length()
if ( l > _maxSpeed ) {
this.velocity.divideScalar( l / _maxSpeed )
}
this.position.add( this.velocity )
_acceleration.set( 0, 0, 0 )
}
this.checkBounds = function () {
if ( this.position.x > _width ) this.position.x = - _width;
if ( this.position.x < - _width ) this.position.x = _width;
if ( this.position.y > _height ) this.position.y = - _height;
if ( this.position.y < - _height ) this.position.y = _height;
if ( this.position.z > _depth ) this.position.z = - _depth;
if ( this.position.z < - _depth ) this.position.z = _depth;
}
this.avoid = function ( target ) {
var steer = new THREE.Vector3()
steer.copy( this.position )
steer.sub( target )
steer.multiplyScalar( 1 / this.position.distanceToSquared( target ) )
return steer
}
this.repulse = function ( target ) {
var distance = this.position.distanceTo( target )
if ( distance < 150 ) {
var steer = new THREE.Vector3()
steer.subVectors( this.position, target )
steer.multiplyScalar( 0.5 / distance )
_acceleration.add( steer )
}
}
this.reach = function ( target, amount ) {
var steer = new THREE.Vector3()
steer.subVectors( target, this.position )
steer.multiplyScalar( amount )
return steer
}
this.alignment = function ( boids ) {
var boid, velSum = new THREE.Vector3(), count = 0, distance
const radius = _neighborhoodRadius * _options.alignment/20
for ( var i = 0, il = boids.length; i < il; i++ ) {
if ( Math.random() > 0.6 ) continue
boid = boids[ i ]
distance = boid.position.distanceTo( this.position )
if ( distance > 0 && distance <= radius ) {
velSum.add( boid.velocity )
count++
}
}
if ( count > 0 ) {
velSum.divideScalar( count )
var l = velSum.length()
if ( l > _maxSteerForce ) {
velSum.divideScalar( l / _maxSteerForce )
}
}
return velSum
}
this.cohesion = function ( boids ) {
var boid, distance,
posSum = new THREE.Vector3(),
steer = new THREE.Vector3(),
count = 0
const radius = _neighborhoodRadius * _options.cohesion/20
for ( var i = 0, il = boids.length; i < il; i ++ ) {
if ( Math.random() > 0.6 ) continue
boid = boids[ i ]
distance = boid.position.distanceTo( this.position )
if ( distance > 0 && distance <= radius ) {
posSum.add( boid.position )
count++
}
}
if ( count > 0 ) {
posSum.divideScalar( count )
}
steer.subVectors( posSum, this.position )
var l = steer.length()
if ( l > _maxSteerForce ) {
steer.divideScalar( l / _maxSteerForce )
}
return steer
}
this.separation = function ( boids ) {
var boid, distance,
posSum = new THREE.Vector3(),
repulse = new THREE.Vector3()
const radius = _neighborhoodRadius * _options.separation/20
for ( var i = 0, il = boids.length; i < il; i ++ ) {
if ( Math.random() > 0.6 ) continue
boid = boids[ i ]
distance = boid.position.distanceTo( this.position )
if ( distance > 0 && distance <= radius ) {
repulse.subVectors( this.position, boid.position )
repulse.normalize()
repulse.divideScalar( distance )
posSum.add( repulse )
}
}
return posSum
}
}
THREE.BirdGeometry = function(options) {
if (options.quantity) {
WIDTH = Math.pow(2, options.quantity)
BIRDS = WIDTH * WIDTH
}
const triangles = BIRDS * 3
const points = triangles * 3
THREE.BufferGeometry.call(this)
const vertices = new THREE.BufferAttribute(new Float32Array(points * 3), 3)
const birdColors = new THREE.BufferAttribute(new Float32Array(points * 3), 3)
const references = new THREE.BufferAttribute(new Float32Array(points * 2), 2)
const birdVertex = new THREE.BufferAttribute(new Float32Array(points), 1)
if (!this.setAttribute) this.setAttribute = this.addAttribute // handle three.js migration r109 → r110
this.setAttribute('position', vertices)
this.setAttribute('birdColor', birdColors)
this.setAttribute('reference', references)
this.setAttribute('birdVertex', birdVertex)
// this.addAttribute( 'normal', new Float32Array( points * 3 ), 3 )
let v = 0
const verts_push = function() {
for (let i=0; i<arguments.length; i++) {
vertices.array[v++] = arguments[i]
}
}
const wingSpan = options.wingSpan || 20
const s = options.birdSize || 1
for (let f=0; f<BIRDS; f++) {
verts_push(0, -0, -20*s, 0, 4*s, -20*s, 0, 0, 30*s) // Body
verts_push(0, 0, -15*s, -wingSpan*s, 0, 0, 0, 0, 15*s) // Left Wing
verts_push(0, 0, 15*s, wingSpan*s, 0, 0, 0, 0, -15*s) // Right Wing
}
const colorCache = {}
for (v=0; v<triangles*3; v++) {
const i = ~~(v / 3)
const x = (i % WIDTH) / WIDTH
const y = ~~(i / WIDTH) / WIDTH
const order = ~~(v / 9) / BIRDS
const key = order.toString()
const gradient = options.colorMode.indexOf('Gradient') != -1
let c
if (!gradient && colorCache[key]) {
c = colorCache[key]
} else {
c = options.effect.getNewCol(order)
}
if (!gradient && !colorCache[key]) {
colorCache[key] = c
}
birdColors.array[(v * 3) + 0] = c.r
birdColors.array[(v * 3) + 1] = c.g
birdColors.array[(v * 3) + 2] = c.b
references.array[v * 2] = x
references.array[(v * 2) + 1] = y
birdVertex.array[v] = v % 9
}
return this.scale(0.2, 0.2, 0.2)
}
THREE.BirdGeometry.prototype = Object.create(THREE.BufferGeometry.prototype)
}
const fragmentShaderPosition = `\
uniform float time;
uniform float delta;
void main() {
vec2 uv = gl_FragCoord.xy / resolution.xy;
vec4 tmpPos = texture2D( texturePosition, uv );
vec3 position = tmpPos.xyz;
vec3 velocity = texture2D( textureVelocity, uv ).xyz;
float phase = tmpPos.w;
phase = mod( ( phase + delta +
length( velocity.xz ) * delta * 3. +
max( velocity.y, 0.0 ) * delta * 6. ), 62.83 );
gl_FragColor = vec4( position + velocity * delta * 15. , phase );
}`
const fragmentShaderVelocity = `\
uniform float time;
uniform float testing;
uniform float delta; // about 0.016
uniform float separationDistance; // 20
uniform float alignmentDistance; // 40
uniform float cohesionDistance;
uniform float speedLimit;
uniform float freedomFactor;
uniform vec3 predator;
const float width = resolution.x;
const float height = resolution.y;
const float PI = 3.141592653589793;
const float PI_2 = PI * 2.0;
// const float VISION = PI * 0.55;
float zoneRadius = 40.0;
float zoneRadiusSquared = 1600.0;
float separationThresh = 0.45;
float alignmentThresh = 0.65;
const float UPPER_BOUNDS = BOUNDS;
const float LOWER_BOUNDS = -UPPER_BOUNDS;
float rand(vec2 co){
return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);
}
void main() {
zoneRadius = separationDistance + alignmentDistance + cohesionDistance;
separationThresh = separationDistance / zoneRadius;
alignmentThresh = ( separationDistance + alignmentDistance ) / zoneRadius;
zoneRadiusSquared = zoneRadius * zoneRadius;
vec2 uv = gl_FragCoord.xy / resolution.xy;
vec3 birdPosition, birdVelocity;
vec3 selfPosition = texture2D( texturePosition, uv ).xyz;
vec3 selfVelocity = texture2D( textureVelocity, uv ).xyz;
float dist;
vec3 dir; // direction
float distSquared;
float separationSquared = separationDistance * separationDistance;
float cohesionSquared = cohesionDistance * cohesionDistance;
float f;
float percent;
vec3 velocity = selfVelocity;
float limit = speedLimit;
dir = predator * UPPER_BOUNDS - selfPosition;
dir.z = 0.;
// dir.z *= 0.6;
dist = length( dir );
distSquared = dist * dist;
float preyRadius = 150.0;
float preyRadiusSq = preyRadius * preyRadius;
// move birds away from predator
if (dist < preyRadius) {
f = ( distSquared / preyRadiusSq - 1.0 ) * delta * 100.;
velocity += normalize( dir ) * f;
limit += 5.0;
}
// if (testing == 0.0) {}
// if ( rand( uv + time ) < freedomFactor ) {}
// Attract flocks to the center
vec3 central = vec3( 0., 0., 0. );
dir = selfPosition - central;
dist = length( dir );
dir.y *= 2.5;
velocity -= normalize( dir ) * delta * 5.;
for (float y=0.0;y<height;y++) {
for (float x=0.0;x<width;x++) {
vec2 ref = vec2( x + 0.5, y + 0.5 ) / resolution.xy;
birdPosition = texture2D( texturePosition, ref ).xyz;
dir = birdPosition - selfPosition;
dist = length(dir);
if (dist < 0.0001) continue;
distSquared = dist * dist;
if (distSquared > zoneRadiusSquared ) continue;
percent = distSquared / zoneRadiusSquared;
if ( percent < separationThresh ) { // low
// Separation - Move apart for comfort
f = (separationThresh / percent - 1.0) * delta;
velocity -= normalize(dir) * f;
} else if ( percent < alignmentThresh ) { // high
// Alignment - fly the same direction
float threshDelta = alignmentThresh - separationThresh;
float adjustedPercent = ( percent - separationThresh ) / threshDelta;
birdVelocity = texture2D( textureVelocity, ref ).xyz;
f = ( 0.5 - cos( adjustedPercent * PI_2 ) * 0.5 + 0.5 ) * delta;
velocity += normalize(birdVelocity) * f;
} else {
// Attraction / Cohesion - move closer
float threshDelta = 1.0 - alignmentThresh;
float adjustedPercent = ( percent - alignmentThresh ) / threshDelta;
f = ( 0.5 - ( cos( adjustedPercent * PI_2 ) * -0.5 + 0.5 ) ) * delta;
velocity += normalize(dir) * f;
}
}
}
// this make tends to fly around than down or up
// if (velocity.y > 0.) velocity.y *= (1. - 0.2 * delta);
// Speed Limits
if ( length( velocity ) > limit ) {
velocity = normalize( velocity ) * limit;
}
gl_FragColor = vec4( velocity, 1.0 );
}`
const birdVS = `\
attribute vec2 reference;
attribute float birdVertex;
attribute vec3 birdColor;
uniform sampler2D texturePosition;
uniform sampler2D textureVelocity;
varying vec4 vColor;
varying float z;
uniform float time;
uniform float birdSize;
void main() {
vec4 tmpPos = texture2D( texturePosition, reference );
vec3 pos = tmpPos.xyz;
vec3 velocity = normalize(texture2D( textureVelocity, reference ).xyz);
vec3 newPosition = position;
if ( birdVertex == 4.0 || birdVertex == 7.0 ) {
// flap wings
newPosition.y = sin( tmpPos.w ) * 5. * birdSize;
}
newPosition = mat3( modelMatrix ) * newPosition;
velocity.z *= -1.;
float xz = length( velocity.xz );
float xyz = 1.;
float x = sqrt( 1. - velocity.y * velocity.y );
float cosry = velocity.x / xz;
float sinry = velocity.z / xz;
float cosrz = x / xyz;
float sinrz = velocity.y / xyz;
mat3 maty = mat3(
cosry, 0, -sinry,
0 , 1, 0 ,
sinry, 0, cosry
);
mat3 matz = mat3(
cosrz , sinrz, 0,
-sinrz, cosrz, 0,
0 , 0 , 1
);
newPosition = maty * matz * newPosition;
newPosition += pos;
z = newPosition.z;
vColor = vec4( birdColor, 1.0 );
gl_Position = projectionMatrix * viewMatrix * vec4( newPosition, 1.0 );
}`
const birdFS = `\
varying vec4 vColor;
varying float z;
uniform vec3 color;
void main() {
// Fake colors for now
float rr = 0.2 + ( 1000. - z ) / 1000. * vColor.x;
float gg = 0.2 + ( 1000. - z ) / 1000. * vColor.y;
float bb = 0.2 + ( 1000. - z ) / 1000. * vColor.z;
gl_FragColor = vec4( rr, gg, bb, 1. );
}`
const fillPositionTexture = function(texture) {
const theArray = texture.image.data
let k = 0
const kl = theArray.length
return (() => {
const result = []
while (k < kl) {
const x = (Math.random() * BOUNDS) - BOUNDS_HALF
const y = (Math.random() * BOUNDS) - BOUNDS_HALF
const z = (Math.random() * BOUNDS) - BOUNDS_HALF
theArray[k + 0] = x
theArray[k + 1] = y
theArray[k + 2] = z
theArray[k + 3] = 1
result.push(k += 4)
}
return result
})()
}
const fillVelocityTexture = function(texture) {
const theArray = texture.image.data
let k = 0
const kl = theArray.length
return (() => {
const result = []
while (k < kl) {
const x = Math.random() - 0.5
const y = Math.random() - 0.5
const z = Math.random() - 0.5
theArray[k + 0] = x * 10
theArray[k + 1] = y * 10
theArray[k + 2] = z * 10
theArray[k + 3] = 1
result.push(k += 4)
}
return result
})()
}
class Birds extends VantaBase {
static initClass() {
this.prototype.defaultOptions = {
// Beige: 0xf8e8d0, 0xf50000, 0xcfcf1d
backgroundColor: 0x07192F, // 0x202428
color1: 0xff0000, // 0xf50000 # 0xfa9898
color2: 0x00d1ff, // 0xcfcf1d # 0x8c4646
colorMode: 'varianceGradient',
birdSize: 1,
wingSpan: 30,
speedLimit: 5,
separation: 20,
alignment: 20,
cohesion: 20,
quantity: 5, // range from 2 to 5
}
}
constructor(userOptions) {
THREE = userOptions.THREE || THREE
ATTACH_CLASSES(THREE)
super(userOptions)
}
initComputeRenderer() {
this.gpuCompute = new GPUComputationRenderer(WIDTH, WIDTH, this.renderer, THREE)
const dtPosition = this.gpuCompute.createTexture()
const dtVelocity = this.gpuCompute.createTexture()
fillPositionTexture(dtPosition)
fillVelocityTexture(dtVelocity)
this.velocityVariable = this.gpuCompute.addVariable('textureVelocity', fragmentShaderVelocity, dtVelocity)
this.positionVariable = this.gpuCompute.addVariable('texturePosition', fragmentShaderPosition, dtPosition)
this.gpuCompute.setVariableDependencies(this.velocityVariable, [
this.positionVariable,
this.velocityVariable
])
this.gpuCompute.setVariableDependencies(this.positionVariable, [
this.positionVariable,
this.velocityVariable
])
this.positionUniforms = this.positionVariable.material.uniforms
this.velocityUniforms = this.velocityVariable.material.uniforms
this.positionUniforms.time = {value: 0.0}
this.positionUniforms.delta = {value: 0.0}
this.velocityUniforms.time = {value: 1.0}
this.velocityUniforms.delta = {value: 0.0}
this.velocityUniforms.testing = {value: 1.0}
this.velocityUniforms.separationDistance = {value: 1.0}
this.velocityUniforms.alignmentDistance = {value: 1.0}
this.velocityUniforms.cohesionDistance = {value: 1.0}
this.velocityUniforms.speedLimit = {value: 1.0}
this.velocityUniforms.freedomFactor = {value: 1.0}
this.velocityUniforms.predator = {value: new THREE.Vector3}
this.velocityVariable.material.defines.BOUNDS = BOUNDS.toFixed(2)
this.velocityVariable.wrapS = THREE.RepeatWrapping
this.velocityVariable.wrapT = THREE.RepeatWrapping
this.positionVariable.wrapS = THREE.RepeatWrapping
this.positionVariable.wrapT = THREE.RepeatWrapping
const error = this.gpuCompute.init()
if (error !== null) {
console.error(error)
}
}
initGpgpuBirds() {
const optionsWithEffect = Object.assign({}, this.options, {effect:this})
const geometry = new THREE.BirdGeometry(optionsWithEffect)
// For Vertex and Fragment
this.birdUniforms = {
color: { value: new THREE.Color(0xff2200) },
texturePosition: { value: null },
textureVelocity: { value: null },
time: { value: 1.0 },
delta: { value: 0.0 },
birdSize: {value: this.options.birdSize}
}
// ShaderMaterial
const material = new THREE.ShaderMaterial({
uniforms: this.birdUniforms,
vertexShader: birdVS,
fragmentShader: birdFS,
side: THREE.DoubleSide
});
const birdMesh = new THREE.Mesh(geometry, material)
birdMesh.rotation.y = Math.PI / 2
birdMesh.matrixAutoUpdate = false
birdMesh.updateMatrix()
return this.scene.add(birdMesh)
}
getNewCol(order) {
const options = this.options
const color1 = options.color1 != null ? options.color1 : 0x440000
const color2 = options.color2 != null ? options.color2 : 0x660000
const c1 = new THREE.Color(color1)
const c2 = new THREE.Color(color2)
const gradient = options.colorMode.indexOf('Gradient') != -1
let c, dist
if (gradient) {
// each vertex has a different color
dist = Math.random()
} else {
// each vertex has the same color
dist = order
}
if (options.colorMode.indexOf('variance') == 0) {
const r2 = (c1.r + Math.random() * c2.r).clamp(0,1)
const g2 = (c1.g + Math.random() * c2.g).clamp(0,1)
const b2 = (c1.b + Math.random() * c2.b).clamp(0,1)
c = new THREE.Color(r2, g2, b2)
} else if (options.colorMode.indexOf('mix') == 0) {
// Naive color arithmetic
c = new THREE.Color(color1 + dist * color2)
} else {
// Linear interpolation
c = c1.lerp(c2, dist)
}
return c
}
onInit() {
this.camera = new THREE.PerspectiveCamera( 75, this.width / this.height, 1, 3000 )
this.camera.position.z = 350
this.fog = new THREE.Fog( 0xffffff, 100, 1000 )
this.mouseX = (this.mouseY = 0)
const birds = this.birds = []
const boids = this.boids = []
const options = this.options
let boid, bird
if (GPGPU) {
try {
this.initComputeRenderer()
this.valuesChanger = this.valuesChanger.bind(this)
this.valuesChanger()
this.initGpgpuBirds()
} catch (err) {
console.error('[vanta.js] birds init error: ', err)
}
} else {
const numBirds = 6 * Math.pow(2, options.quantity)
for (var i = 0; i < numBirds; i++) {
boid = boids[i] = new Boid(options)
boid.position.x = Math.random() * 400 - 200
boid.position.y = Math.random() * 400 - 200
boid.position.z = Math.random() * 400 - 200
boid.velocity.x = Math.random() * 2 - 1
boid.velocity.y = Math.random() * 2 - 1
boid.velocity.z = Math.random() * 2 - 1
boid.setWorldSize( 500, 500, 300 )
const gradient = options.colorMode.indexOf('Gradient') != -1
const newBirdGeo = new Bird(options)
for (var j=0; j < newBirdGeo.faces.length; j++) {
if (gradient) {
for (var k=0; k<3; k++) {
const newColor = this.getNewCol()
newBirdGeo.faces[j].vertexColors[k] = newColor
}
} else {
const newColor = this.getNewCol(i/numBirds)
newBirdGeo.faces[j].vertexColors[0] = newColor
newBirdGeo.faces[j].vertexColors[1] = newColor
newBirdGeo.faces[j].vertexColors[2] = newColor
}
}
bird = birds[i] = new THREE.Mesh(
newBirdGeo,
new THREE.MeshBasicMaterial( {
color: 0xffffff,
side: THREE.DoubleSide,
// colors: THREE.VertexColors,
vertexColors: THREE.VertexColors,
}))
bird.phase = Math.floor( Math.random() * 62.83 )
bird.position.x = boids[i].position.x
bird.position.y = boids[i].position.y
bird.position.z = boids[i].position.z
this.scene.add( bird )
// if (i == 0) {
// window.bird = bird; window.boid = boid;
// }
}
}
}
valuesChanger() {
if (this.velocityUniforms) {
this.velocityUniforms.separationDistance.value = this.options.separation
this.velocityUniforms.alignmentDistance.value = this.options.alignment
this.velocityUniforms.speedLimit.value = this.options.speedLimit
this.velocityUniforms.cohesionDistance.value = this.options.cohesion
}
}
onUpdate() {
this.now = performance.now()
if (!this.last) { this.last = this.now }
let delta = (this.now - this.last) / 1000
if (delta > 1) { delta = 1 }
this.last = this.now
if (GPGPU) {
this.positionUniforms.time.value = this.now
this.positionUniforms.delta.value = delta
this.velocityUniforms.time.value = this.now
this.velocityUniforms.delta.value = delta
this.birdUniforms.time.value = this.now
this.birdUniforms.delta.value = delta
this.velocityUniforms.predator.value.set(
this.mouseX,
-this.mouseY,
0
)
this.mouseX = 10000
this.mouseY = 10000
this.gpuCompute.compute()
this.birdUniforms.texturePosition.value = this.gpuCompute.getCurrentRenderTarget( this.positionVariable ).texture
this.birdUniforms.textureVelocity.value = this.gpuCompute.getCurrentRenderTarget( this.velocityVariable ).texture
} else {
const birds = this.birds
const boids = this.boids
let boid, bird, color
for ( var i = 0, il = birds.length; i < il; i++ ) {
boid = boids[ i ]
boid.run( boids )
bird = birds[ i ]
// color = bird.material.color
// color.r = color.g = color.b = ( 500 - bird.position.z ) / 1000
bird.rotation.y = Math.atan2( - boid.velocity.z, boid.velocity.x )
bird.rotation.z = Math.asin( boid.velocity.y / boid.velocity.length() )
// Flapping
bird.phase = (bird.phase + (Math.max(0, bird.rotation.z)+0.1)) % 62.83
bird.geometry.vertices[5].y = bird.geometry.vertices[4].y =
Math.sin( bird.phase ) * 5 * this.options.birdSize
bird.geometry.verticesNeedUpdate = true
bird.position.x = boids[i].position.x
bird.position.y = boids[i].position.y
bird.position.z = boids[i].position.z
}
}
}
onMouseMove(x,y) {
// Center on 0,0
this.mouseX = x - 0.5
this.mouseY = y - 0.5
if (!GPGPU) {
const boids = this.boids
let boid
var vector = new THREE.Vector3( this.mouseX * this.width, -this.mouseY * this.height, 0 );
for ( var i = 0, il = boids.length; i < il; i++ ) {
boid = boids[i]
vector.z = boid.position.z
boid.repulse( vector )
}
}
}
onDestroy() {}
onResize() {}
}
Birds.initClass()
export default VANTA.register('BIRDS', Birds)