https://twitter.com/matths/status/1337716351729799169
couldn't resist to do a threejs/webgl http://tixy.land renderer based on the ripple codepen of @ycwhk
A Pen by jupegarnica on CodePen.
| <div class="buttons"> | |
| <button onclick="prevTixy()"><</button> | |
| <button onclick="nextTixy()">></button> | |
| </div> |
| import * as $ from '//unpkg.com/[email protected]/build/three.module.js' | |
| import { OrbitControls } from '//unpkg.com/[email protected]/examples/jsm/controls/OrbitControls.js' | |
| import { EffectComposer } from '//unpkg.com/[email protected]/examples/jsm/postprocessing/EffectComposer' | |
| import { RenderPass } from '//unpkg.com/[email protected]/examples/jsm/postprocessing/RenderPass' | |
| import { UnrealBloomPass } from '//unpkg.com/[email protected]/examples/jsm/postprocessing/UnrealBloomPass' | |
| import { BokehPass } from '//unpkg.com/[email protected]/examples/jsm/postprocessing/BokehPass' | |
| const renderer = new $.WebGLRenderer({ antialias: true }); | |
| const scene = new $.Scene(); | |
| const camera = new $.PerspectiveCamera(45, 2, .1, 1000); | |
| const controls = new OrbitControls(camera, renderer.domElement); | |
| const composer = new EffectComposer(renderer); | |
| const drawingBufferSize = new $.Vector2(); | |
| window.addEventListener('resize', () => { | |
| const { clientWidth, clientHeight } = renderer.domElement; | |
| renderer.setPixelRatio(window.devicePixelRatio); | |
| renderer.setSize(clientWidth, clientHeight, false); | |
| camera.aspect = clientWidth / clientHeight; | |
| camera.updateProjectionMatrix(); | |
| composer.setPixelRatio(window.devicePixelRatio); | |
| composer.setSize(clientWidth, clientHeight); | |
| renderer.getDrawingBufferSize(drawingBufferSize); | |
| }); | |
| document.body.append(renderer.domElement); | |
| window.dispatchEvent(new Event('resize')); | |
| const SIZE = 16; | |
| const ITEM_BASE_SIZE = 1.8; | |
| scene.background = new $.Color('rgba(30,30,30)'); | |
| camera.position.set(-30, 60, -30); | |
| controls.autoRotate = true; | |
| controls.autoRotateSpeed = 0.5; | |
| const light = new $.DirectionalLight('white', 1); | |
| light.position.set( -3, 15, -1 ); | |
| light.castShadow = true; | |
| scene.add(light); | |
| const limit = (v) => Math.max(Math.min(v,1),-1); | |
| class Inst { | |
| constructor(mesh, idx, r, g, b, x, z) { | |
| this.mesh = mesh; | |
| this.idx = idx; | |
| this.color = new $.Color(r, g, b); | |
| this.matrix = new $.Matrix4(); | |
| this.translateMatrix = new $.Matrix4(); | |
| this.scaleMatrix = new $.Matrix4(); | |
| this.x = x; | |
| this.z = z; | |
| this.matrix.makeTranslation(x, 0, z); | |
| this.updateColor(); | |
| this.updateMatrix(); | |
| } | |
| setValue(v) { | |
| v = limit(v); | |
| if (v>=0) { | |
| this.color.r = v; | |
| this.color.g = v; | |
| this.color.b = v; | |
| } else { | |
| this.color.r = Math.abs(v); | |
| this.color.g = 0; | |
| this.color.b = 0; | |
| } | |
| this.updateColor(); | |
| const y = v*5; | |
| const s = Math.abs(v); | |
| this.translateMatrix.makeTranslation(1, y, 1); | |
| this.scaleMatrix.makeScale(s, s, s); | |
| this.updateMatrix(); | |
| } | |
| updateColor() { | |
| this.mesh.setColorAt(this.idx, this.color); | |
| this.mesh.instanceColor.needsUpdate = true; | |
| } | |
| updateMatrix() { | |
| this.matrix.makeTranslation(this.x, 0, this.z) | |
| .multiply(this.translateMatrix) | |
| .multiply(this.scaleMatrix); | |
| this.mesh.setMatrixAt(this.idx, this.matrix); | |
| this.mesh.instanceMatrix.needsUpdate = true; | |
| } | |
| } | |
| const geom = new $.SphereBufferGeometry( 1, 16, 16 ).translate(0, 0.5, 0); | |
| const mat = new $.MeshLambertMaterial(); | |
| const mesh = new $.InstancedMesh(geom, mat, SIZE * SIZE); | |
| scene.add(mesh); | |
| const insts = []; | |
| for (let i = 0, I = SIZE; i < I; ++i) { | |
| for (let j = 0, J = SIZE; j < J; ++j) { | |
| const n = i * SIZE + j; | |
| const x = (j / J - 0.5) * (ITEM_BASE_SIZE * 1.01 * SIZE); | |
| const z = (i / I - 0.5) * (ITEM_BASE_SIZE * 1.01 * SIZE); | |
| insts.push(new Inst(mesh, n, 0, 0, 0, x, z)); | |
| } | |
| } | |
| const tixy = [ | |
| (t, i, x, y) => Math.sin(t), | |
| (t, i, x, y) => Math.random() < 0.3, | |
| (t, i, x, y) => Math.sin(t-Math.sqrt((x-7.5)**2+(y-6)**2)), | |
| (t, i, x, y) => Math.sin(y/8 + t), | |
| (t, i, x, y) => -.4/(Math.hypot(x-t%10,y-t%8)-t%2*9), | |
| (t, i, x, y) => Math.sin(2*Math.atan((y-7.5)/(x-7.5))+5*t) | |
| ] | |
| let current = 5; | |
| let transform = tixy[current]; | |
| window.prevTixy = () => { | |
| current--; | |
| if (current<0) current = tixy.length - 1; | |
| transform = tixy[current]; | |
| } | |
| window.nextTixy = () => { | |
| current++; | |
| if (current>=tixy.length) current = 0; | |
| transform = tixy[current]; | |
| } | |
| const size = SIZE; | |
| const loop = () => { | |
| const t = window.performance.now()/1000; | |
| insts.forEach((inst, i) => { | |
| const x = i%size; | |
| const y = Math.floor(i/size); | |
| const v = transform(0.66*t, i, x, y); | |
| inst.setValue(v); | |
| }) | |
| } | |
| renderer.setAnimationLoop(() => { | |
| composer.render(); | |
| controls.update(); | |
| loop(); | |
| }); | |
| const renderPass = new RenderPass(scene, camera); | |
| const bokehPass = new BokehPass(scene, camera, { focus: 40, aperture: 0.01 / 20, maxblur: 0.01 }); | |
| const bloomPass = new UnrealBloomPass(drawingBufferSize, 0.5, 0.2, 0.2); | |
| composer.addPass(renderPass); | |
| //composer.addPass(bloomPass); | |
| //composer.addPass(bokehPass); |
| <script src="https://unpkg.co/gsap@3/dist/gsap.min.js"></script> |
| canvas { | |
| display: block; width: 100vw; height: 100vh; | |
| cursor: grab; | |
| } | |
| .buttons { | |
| position: absolute; | |
| padding: 10px; | |
| } | |
| button { | |
| margin: 5px; | |
| background: none; | |
| border: 1px solid #fff; | |
| color: white; | |
| font-family: monospace; | |
| font-size: 24px; | |
| } |
https://twitter.com/matths/status/1337716351729799169
couldn't resist to do a threejs/webgl http://tixy.land renderer based on the ripple codepen of @ycwhk
A Pen by jupegarnica on CodePen.