1class Particles { 2 3 constructor(webgl) { 4 this.webgl = webgl; 5 this.container = new THREE.Object3D(); 6 } 7 8 init(src) { 9 const loader = new THREE.TextureLoader(); 10 11 loader.load(src, (texture) => { 12 this.texture = texture; 13 this.texture.minFilter = THREE.LinearFilter; 14 this.texture.magFilter = THREE.LinearFilter; 15 this.texture.format = THREE.RGBFormat; 16 17 this.width = texture.image.width; 18 this.height = texture.image.height; 19 20 // hardcoded to improve browser compatibility 21 this.width = 151; 22 this.height = 150; 23 24 this.initPoints(true); 25 this.initHitArea(); 26 this.initTouch(); 27 this.resize(); 28 this.show(); 29 }); 30 31 var self = this; 32 33 $(window).resize(function(){ 34 setTimeout(function(){ 35 self.resize(); 36 }, 500); 37 }); 38 } 39 40 initPoints(discard) { 41 this.numPoints = this.width * this.height; 42 43 let numVisible = this.numPoints; 44 let threshold = 0; 45 let originalColors; 46 47 if (discard ) { 48 // discard pixels darker than threshold #22 49 numVisible = 0; 50 threshold = 34; 51 52 const img = this.texture.image; 53 const canvas = document.createElement('canvas'); 54 const ctx = canvas.getContext('2d'); 55 56 canvas.width = this.width; 57 canvas.height = this.height; 58 ctx.scale(1, -1); 59 ctx.drawImage(img, 0, 0, this.width, this.height * -1); 60 61 const imgData = ctx.getImageData(0, 0, canvas.width, canvas.height); 62 originalColors = Float32Array.from(imgData.data); 63 64 for (let i = 0; i < this.numPoints; i++) { 65 if (originalColors[i * 4 + 0] > threshold) numVisible++; 66 } 67 68 // console.log('numVisible', numVisible, this.numPoints); 69 } 70 71 const uniforms = { 72 uTime: { value: 0 }, 73 uRotation: { value: this.getThreejsVector(0) }, 74 uRandom: { value: 1.0 }, 75 uDepth: { value: 2.0 }, 76 uDepthModifier: { value: 0.0 }, 77 uSize: { value: 0.0 }, 78 uSizeDescrease: { value: 0.0 }, 79 uTextureSize: { value: new THREE.Vector2(this.width, this.height) }, 80 uResolution: { value: new THREE.Vector2(this.webgl.renderer.domElement.width, this.webgl.renderer.domElement.height) }, 81 uTexture: { value: this.texture }, 82 uTouch: { value: null }, 83 }; 84 85 const material = new THREE.RawShaderMaterial({ 86 uniforms, 87 vertexShader: vertexShader, 88 fragmentShader: fragmentShader, 89 depthTest: false, 90 transparent: true, 91 blending: THREE.AdditiveBlending 92 }); 93 94 const geometry = new THREE.InstancedBufferGeometry(); 95 96 // positions 97 const positions = new THREE.BufferAttribute(new Float32Array(4 * 3), 3); 98 positions.setXYZ(0, -0.5, 0.5, 0.0); 99 positions.setXYZ(1, 0.5, 0.5, 0.0); 100 positions.setXYZ(2, -0.5, -0.5, 0.0); 101 positions.setXYZ(3, 0.5, -0.5, 0.0); 102 geometry.addAttribute('position', positions); 103 104 // uvs 105 const uvs = new THREE.BufferAttribute(new Float32Array(4 * 2), 2); 106 uvs.setXYZ(0, 0.0, 0.0); 107 uvs.setXYZ(1, 1.0, 0.0); 108 uvs.setXYZ(2, 0.0, 1.0); 109 uvs.setXYZ(3, 1.0, 1.0); 110 geometry.addAttribute('uv', uvs); 111 112 // index 113 geometry.setIndex(new THREE.BufferAttribute(new Uint16Array([ 0, 2, 1, 2, 3, 1 ]), 1)); 114 115 const indices = new Uint16Array(numVisible); 116 const offsets = new Float32Array(numVisible * 3); 117 const angles = new Float32Array(numVisible); 118 119 for (let i = 0, j = 0; i < this.numPoints; i++) { 120 if (discard && originalColors[i * 4 + 0] <= threshold) continue; 121 122 offsets[j * 3 + 0] = i % this.width; 123 offsets[j * 3 + 1] = Math.floor(i / this.width); 124 125 indices[j] = i; 126 127 angles[j] = Math.random() * Math.PI; 128 129 j++; 130 } 131 132 geometry.addAttribute('pindex', new THREE.InstancedBufferAttribute(indices, 1, false)); 133 geometry.addAttribute('offset', new THREE.InstancedBufferAttribute(offsets, 3, false)); 134 geometry.addAttribute('angle', new THREE.InstancedBufferAttribute(angles, 1, false)); 135 136 this.object3D = new THREE.Mesh(geometry, material); 137 this.container.add(this.object3D); 138 } 139 140 getThreejsVector(angleInDegrees) { 141 const angleInRadians = angleInDegrees * Math.PI / 180; 142 const s = Math.sin(angleInRadians); 143 const c = Math.cos(angleInRadians); 144 return new THREE.Vector2(s, c); 145 } 146 147 initTouch() { 148 // create only once 149 // if (!this.touch) this.touch = new TouchTexture(this); 150 // this.object3D.material.uniforms.uTouch.value = this.touch.texture; 151 } 152 153 initHitArea() { 154 const geometry = new THREE.PlaneGeometry(this.width, this.height, 1, 1); 155 const material = new THREE.MeshBasicMaterial({ color: 0xFFFFFF, wireframe: true, depthTest: false }); 156 material.visible = false;
157 this.hitArea = new THREE.Mesh(geometry, material); 158 this.container.add(this.hitArea); 159 } 160 161 addListeners() { 162 this.handlerInteractiveMove = this.onInteractiveMove.bind(this); 163 164 if(!this.webgl.interactive) { 165 return; 166 } 167 this.webgl.interactive.addListener('interactive-move', this.handlerInteractiveMove); 168 this.webgl.interactive.objects.push(this.hitArea); 169 this.webgl.interactive.enable(); 170 } 171 172 removeListeners() { 173 if(!this.webgl.interactive) { 174 return; 175 } 176 this.webgl.interactive.removeListener('interactive-move', this.handlerInteractiveMove); 177 const index = this.webgl.interactive.objects.findIndex(obj => obj === this.hitArea); 178 this.webgl.interactive.objects.splice(index, 1); 179 this.webgl.interactive.disable(); 180 } 181 182 // --------------------------------------------------------------------------------------------- 183 // PUBLIC 184 // --------------------------------------------------------------------------------------------- 185 186 update(delta) { 187 if (!this.object3D) return; 188 // if (this.touch) this.touch.update(); 189 190 this.object3D.material.uniforms.uTime.value += delta; 191 } 192 193 show(time = 1.0) { 194 var self = this; 195 // reset 196 var targetParticleSize = 0.5; 197 gsap.fromTo(this.object3D.material.uniforms.uSize, { duration: time, value: targetParticleSize }, { value: targetParticleSize }); 198 gsap.to(this.object3D.material.uniforms.uRandom, { duration: time, value: 0.5 }); 199 200 var container = this.webgl.app.container; 201 202 203 var tlIn = gsap.timeline({ 204 scrollTrigger: { 205 trigger: container.querySelector(".ias-2023-4d-logo-inner"), 206 start: "bottom bottom", 207 pin: true, 208 pinSpacing: false, 209 endTrigger: container.querySelector('.ias-2023-image-grid-title'), 210 // end: "top-=60% center", 211 end: "top center", // [trigger] [scroller] positions 212 scrub: true, 213 // markers: true, 214 invalidateOnRefresh: true, // invalidate on resize 215 } 216 }); 217 218 tlIn.fromTo(container.querySelector(".ias-2023-4d-logo-holder"), time * 0.7, { autoAlpha: 0 }, { autoAlpha: 1.0 }, 0); 219 tlIn.fromTo(this.object3D.material.uniforms.uDepth, time, { value: 4*150.0 }, { value: 4.0 }, 0); 220 221 var rotationObj = { 222 value: 180 223 }; 224 225 tlIn.fromTo(rotationObj, time, { 226 value: 180 227 }, { 228 value: 0, 229 onUpdate() { 230 self.object3D.material.uniforms.uRotation.value = self.getThreejsVector(rotationObj.value); 231 } 232 }, 0); 233 234 tlIn.fromTo(container.querySelector(".ias-2023-4d-logo"), { duration: 0.3, opacity: 0 }, { opacity: 1 }, "-=0.3"); 235 236 // timeline out 237 var tlOut = gsap.timeline({ 238 scrollTrigger: { 239 trigger: container.querySelector('.ias-2023-image-grid-wrapper'), 240 start: "top bottom", // trigger, scroller 241 endTrigger: container.querySelector('.ias-2023-image-grid-overlay'), 242 // end: "top bottom", 243 pinnedContainer: container.querySelector(".ias-2023-4d-logo-inner"), 244 // pin: container.querySelector(".ias-2023-4d-logo-inner"), 245 pinSpacing: false, 246 end: "bottom top", 247 scrub: true, 248 // markers: true, 249 invalidateOnRefresh: true, // invalidate on resize 250 } 251 }); 252 253 // pin 254 gsap.timeline({ 255 scrollTrigger: { 256 trigger: container.querySelector('.ias-2023-image-grid-wrapper'), 257 start: "top-=25% bottom", // trigger, scroller 258 endTrigger: $(container).next()[0], 259 // endTrigger: container.querySelector('.ias-2023-image-grid-overlay'), 260 end: "bottom bottom", 261 // pinnedContainer: container.querySelector(".ias-2023-4d-logo-inner"), 262 // pin: container.querySelector(".ias-2023-4d-logo-positioner"), 263 pinnedContainer: container.querySelector(".ias-2023-4d-logo-inner"), 264 pin: container.querySelector(".ias-2023-4d-logo-inner"), 265 // pin: container.querySelector(".ias-2023-4d-logo-wrapper"), 266 pinSpacing: false, 267 scrub: true, 268 // markers: true, 269 invalidateOnRefresh: true, // invalidate on resize 270 } 271 }); 272 273 tlOut.fromTo(this.object3D.material.uniforms.uDepthModifier, time, { value: 0.0 }, { value: 150.0 }); 274 tlOut.fromTo(rotationObj, time, { 275 value: 0 276 }, { 277 value: -180, 278 onUpdate() { 279 self.object3D.material.uniforms.uRotation.value = self.getThreejsVector(rotationObj.value); 280 } 281 }, 0); 282 tlOut.fromTo(container.querySelector(".ias-2023-4d-logo img"), time*0.1, {opacity: 1}, { opacity: 0 }, 0); 283 tlOut.fromTo(container.querySelector('.ias-2023-4d-logo-positioner'), time*0.2, {y: 0}, {y: "30%"}); 284 tlOut.fromTo(this.object3D.material.uniforms.uSizeDescrease, time, { value: 0 }, { value: targetParticleSize * 0.8 }, 0); 285 286 window.timelines = window.timelines || []; 287 window.timelines.push(tlOut); 288 289 this.addListeners(); 290 } 291 292 // hide(_destroy, time = 0.8) { 293 // return new Promise((resolve, reject) => { 294 // TweenLite.to(this.object3D.material.uniforms.uRandom, time, { value: 5.0, onComplete: () => { 295 // if (_destroy) this.destroy(); 296 // resolve(); 297 // } }); 298 // TweenLite.to(this.object3D.material.uniforms.uDepth, time, { value: -20.0, ease: Quad.easeIn }); 299 // TweenLite.to(this.object3D.material.uniforms.uSize, time * 0.8, { value: 0.0 }); 300 301 // this.removeListeners(); 302 // }); 303 // } 304 305 destroy() { 306 if (!this.object3D) return; 307 308 this.object3D.parent.remove(this.object3D); 309 this.object3D.geometry.dispose(); 310 this.object3D.material.dispose(); 311 this.object3D = null; 312 313 if (!this.hitArea) return; 314 315 this.hitArea.parent.remove(this.hitArea); 316 this.hitArea.geometry.dispose(); 317 this.hitArea.material.dispose(); 318 this.hitArea = null; 319 } 320 321 // --------------------------------------------------------------------------------------------- 322 // EVENT HANDLERS 323 // --------------------------------------------------------------------------------------------- 324 325 resize() { 326 if (!this.object3D) return; 327 var scale = this.webgl.fovHeight / this.height * 0.4; 328 if(this.webgl.app.container.querySelector('.ias-2023-4d-logo__human-rights')) { 329 scale = this.webgl.fovHeight / this.height * 0.43; 330 } 331 if($(window).width() <= 1024) { 332 scale = this.webgl.fovHeight / this.height * 0.45; 333 } 334 if($(window).width() < 992) { 335 var windowHeight = Math.max(window.innerHeight || 0, window.outerHeight || 0
335); 336 scale = this.webgl.fovHeight / this.height / windowHeight * 250; 337 } 338 339 this.object3D.scale.set(scale, scale, 1); 340 this.hitArea.scale.set(scale, scale, 1); 341 this.object3D.material.uniforms.uResolution.value.x = this.webgl.renderer.domElement.width; 342 this.object3D.material.uniforms.uResolution.value.y = this.webgl.renderer.domElement.height; 343 } 344 345 onInteractiveMove(e) { 346 const uv = e.intersectionData.uv; 347 // if (this.touch) this.touch.addTouch(uv); 348 } 349} 350 351var vertexShader = `// @author brunoimbrizi / http://brunoimbrizi.com 352 353precision highp float; 354#define GLSLIFY 1 355 356attribute float pindex; 357attribute vec3 position; 358attribute vec3 offset; 359attribute vec2 uv; 360attribute float angle; 361 362uniform mat4 modelViewMatrix; 363uniform mat4 projectionMatrix; 364 365uniform float uTime; 366uniform float uRandom; 367uniform float uDepth; 368uniform float uDepthModifier; 369uniform float uSize; 370uniform float uSizeDescrease; 371uniform vec2 uTextureSize; 372uniform sampler2D uTexture; 373uniform sampler2D uTouch; 374uniform vec2 uRotation; 375 376varying vec2 vPUv; 377varying vec2 vUv; 378 379// 380// Description : Array and textureless GLSL 2D simplex noise function. 381// Author : Ian McEwan, Ashima Arts. 382// Maintainer : ijm 383// Lastmod : 20110822 (ijm) 384// License : Copyright (C) 2011 Ashima Arts. All rights reserved. 385// Distributed under the MIT License. See LICENSE file. 386// https://github.com/ashima/webgl-noise 387// 388 389vec3 mod289_1_0(vec3 x) { 390 return x - floor(x * (1.0 / 289.0)) * 289.0; 391} 392 393vec2 mod289_1_0(vec2 x) { 394 return x - floor(x * (1.0 / 289.0)) * 289.0; 395} 396 397vec3 permute_1_1(vec3 x) { 398 return mod289_1_0(((x*34.0)+1.0)*x); 399} 400 401float snoise_1_2(vec2 v) 402 { 403 const vec4 C = vec4(0.211324865405187, // (3.0-sqrt(3.0))/6.0 404 0.366025403784439, // 0.5*(sqrt(3.0)-1.0) 405 -0.577350269189626, // -1.0 + 2.0 * C.x 406 0.024390243902439); // 1.0 / 41.0 407// First corner 408 vec2 i = floor(v + dot(v, C.yy) ); 409 vec2 x0 = v - i + dot(i, C.xx); 410 411// Other corners 412 vec2 i1; 413 //i1.x = step( x0.y, x0.x ); // x0.x > x0.y ? 1.0 : 0.0 414 //i1.y = 1.0 - i1.x; 415 i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0); 416 // x0 = x0 - 0.0 + 0.0 * C.xx ; 417 // x1 = x0 - i1 + 1.0 * C.xx ; 418 // x2 = x0 - 1.0 + 2.0 * C.xx ; 419 vec4 x12 = x0.xyxy + C.xxzz; 420 x12.xy -= i1; 421 422// Permutations 423 i = mod289_1_0(i); // Avoid truncation effects in permutation 424 vec3 p = permute_1_1( permute_1_1( i.y + vec3(0.0, i1.y, 1.0 )) 425 + i.x + vec3(0.0, i1.x, 1.0 )); 426 427 vec3 m = max(0.5 - vec3(dot(x0,x0), dot(x12.xy,x12.xy), dot(x12.zw,x12.zw)), 0.0); 428 m = m*m ; 429 m = m*m ; 430 431// Gradients: 41 points uniformly over a line, mapped onto a diamond. 432// The ring size 17*17 = 289 is close to a multiple of 41 (41*7 = 287) 433 434 vec3 x = 2.0 * fract(p * C.www) - 1.0; 435 vec3 h = abs(x) - 0.5; 436 vec3 ox = floor(x + 0.5); 437 vec3 a0 = x - ox; 438 439// Normalise gradients implicitly by scaling m 440// Approximation of: m *= inversesqrt( a0*a0 + h*h ); 441 m *= 1.79284291400159 - 0.85373472095314 * ( a0*a0 + h*h ); 442 443// Compute final noise value at P 444 vec3 g; 445 g.x = a0.x * x0.x + h.x * x0.y; 446 g.yz = a0.yz * x12.xz + h.yz * x12.yw; 447 return 130.0 * dot(m, g); 448} 449 450 451 452float random(float n) { 453 return fract(sin(n) * 43758.5453123); 454} 455 456vec2 adjustRotation33(vec2 rotation, float displacedZ) { 457 458 vec2 target = vec2(0.0, 1.0); 459 460 float dist = abs(displacedZ); 461 462 vec2 diff = rotation - target; 463 464 vec2 adjusted = mix(rotation, target, smoothstep(0.0, 1.0, dist)); 465 466 return adjusted; 467 468 } 469 470 vec2 adjustRotation(vec2 uRotation, float displacedZ) { 471 472 float targetZ = 0.0; // Target z coordinate value 473 vec2 targetRotation = vec2(0.0, 1.0); // Target rotation 474 475 // Calculate the weight based on the proximity of displacedZ to targetZ 476 // float weight = 1.0 - abs(displacedZ - targetZ); 477 float weight = 1.0 - smoothstep(0.0, 50.0, abs(displacedZ - targetZ)); 478 479 // Interpolate uRotation towards the target rotation 480 vec2 adjusted = mix(uRotation, targetRotation, weight); 481 482 return adjusted; 483 484 } 485 486void main() { 487 vUv = uv; 488 489 // particle uv 490 vec2 puv = offset.xy / uTextureSize; 491 vPUv = puv; 492 493 // pixel color 494 vec4 colA = texture2D(uTexture, puv); 495 float grey = colA.r * 0.21 + colA.g * 0.71 + colA.b * 0.07; 496 //float grey = colA.r + colA.g + colA.b; 497 498 // displacement 499 vec3 displaced = offset; 500 // randomise 501 displaced.xy += vec2(random(pindex) - 0.5, random(offset.x + pindex) - 0.5) * uRandom; 502 // float rndz = (random(pindex) + snoise_1_2(vec2(pindex * 0.1, uTime * 0.1))); 503 float rndz = (random(pindex) + snoise_1_2(vec2(pindex * 0.01, uTime * 0.005))); 504 displaced.z += rndz * (random(pindex) * 2.0 * (uDepth + uDepthModifier)); 505 // displaced.z += rndz * (2.0 * uDepth); 506 // center 507 displaced.xy -= uTextureSize * 0.5; 508 509 // touch 510 float t = texture2D(uTouch, puv).r; 511 displaced.z += t * 20.0 * rndz; 512 displaced.x += cos(angle) * t * 20.0 * rndz; 513 displaced.y += sin(angle) * t * 20.0 * rndz; 514 515 // particle size 516 float psize = (snoise_1_2(vec2(uTime, pindex) * 0.5) + 2.0); 517 psize *= max(grey, 0.2); 518 psize *= (uSize - uSizeDescrease); 519 520 // Interpolate uRotation towards the target rotation 521 vec2 rotation = adjustRotation(uRotation, displaced.z); 522 // vec2 rotation = uRotation; 523 524 // rotate 525 displaced = vec3( 526 displaced.x * rotation.y + displaced.y * rotation.x,
527 displaced.y * rotation.y - displaced.x * rotation.x, 528 displaced.z); 529 530 // final position 531 vec4 mvPosition = modelViewMatrix * vec4(displaced, 1.0); 532 mvPosition.xyz += position * psize; 533 vec4 finalPosition = projectionMatrix * mvPosition; 534 535 536 gl_Position = finalPosition; 537} 538 539`; 540 541 542var fragmentShader = `// @author brunoimbrizi / http://brunoimbrizi.com 543 544precision highp float; 545 546uniform sampler2D uTexture; 547uniform vec2 uResolution; 548 549 550varying vec2 vPUv; 551varying vec2 vUv; 552 553void main() { 554 vec4 color = vec4(0.0); 555 vec2 uv = vUv; 556 vec2 puv = vPUv; 557 558 // pixel color 559 vec4 colA = texture2D(uTexture, puv); 560 561 // greyscale 562 //float grey = colA.r * 0.21 + colA.g * 0.71 + colA.b * 0.07; 563 //vec4 colB = vec4(grey, grey, grey, 1.0); 564 565 // circle 566 float border = 0.5; 567 float radius = 0.5; 568 float dist = radius - distance(uv, vec2(0.5)); 569 float t = smoothstep(0.0, border, dist); 570 571 // vec2 position = gl_FragCoord.xy / uResolution; 572 vec2 position = gl_FragCoord.xy / uResolution; 573 574 // Fade out particles near the top 575 float fade = 1.0 - smoothstep(0.8, 1.0, position.y); 576 577 // final color 578 color = colA * fade; 579 color.a = t; 580 581 gl_FragColor = color; 582}`;
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.