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/three@0.123.0/build/three.module.js' | |
import { OrbitControls } from '//unpkg.com/three@0.123.0/examples/jsm/controls/OrbitControls.js' | |
import { EffectComposer } from '//unpkg.com/three@0.123.0/examples/jsm/postprocessing/EffectComposer' | |
import { RenderPass } from '//unpkg.com/three@0.123.0/examples/jsm/postprocessing/RenderPass' | |
import { UnrealBloomPass } from '//unpkg.com/three@0.123.0/examples/jsm/postprocessing/UnrealBloomPass' | |
import { BokehPass } from '//unpkg.com/three@0.123.0/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.