| <style> | |
| @import url('https://fonts.googleapis.com/css2?family=Roboto+Slab:wght@900&family=Rokkitt:wght@900&display=swap'); | |
| .text1 { | |
| position: absolute; | |
| top: 3vh; | |
| left: calc(50% - 50vh); | |
| } | |
| .text2 { | |
| position: absolute; | |
| bottom: 4vh; | |
| left: 50%; | |
| } | |
| .retro { | |
| font-family: "Roboto Slab"; | |
| font-size: 13vh; | |
| display: block; | |
| color: #000; | |
| text-shadow: -0.5vh 0 #8800aa, 0 0.5vh #8800aa, 0.5vh 0 #aa0088, 0 -0.5vh #aa0088; | |
| } | |
| </style> | |
| <div class="text1"> | |
| <span class="retro">RETRO</span> | |
| </div> | |
| <div class="text2"> | |
| <span class="retro">WAVE</span> | |
| </div> | |
| <script type="module"> | |
| import * as THREE from "https://cdn.jsdelivr.net/npm/three@0.123.0/build/three.module.js"; | |
| import { OrbitControls } from "https://cdn.jsdelivr.net/npm/three@0.123.0/examples/jsm/controls/OrbitControls.js"; | |
| import { TWEEN } from "https://cdn.jsdelivr.net/npm/three@0.123.0/examples/jsm/libs/tween.module.min.js"; | |
| let scene = new THREE.Scene(); | |
| let camera = new THREE.PerspectiveCamera(60, innerWidth / innerHeight, 1, 100); | |
| camera.position.set(-5, 10, 20); | |
| let renderer = new THREE.WebGLRenderer({antialias: true}); | |
| renderer.setSize(innerWidth, innerHeight); | |
| document.querySelector("div.prose").appendChild(renderer.domElement); | |
| const textureCube = generateCubeMap(); | |
| let controls = new OrbitControls(camera, renderer.domElement); | |
| controls.enableZoom = false; | |
| controls.enablePan = false; | |
| controls.enableKeys = false; | |
| let square = new THREE.GridHelper(20, 1, 0xaaaaff, 0xaaaff); | |
| square.position.y = 0.01; | |
| scene.add(square); | |
| let grid = new THREE.GridHelper(20, 10, "magenta", "magenta"); | |
| console.log(grid.geometry.attributes.position.count); | |
| let moveable = []; | |
| for(let i = 0; i < grid.geometry.attributes.position.count / 4; i++){ | |
| moveable.push(1, 1, 0, 0); | |
| } | |
| console.log(moveable.length) | |
| grid.geometry.setAttribute("moveable", new THREE.Float32BufferAttribute(moveable, 1)); | |
| let uniforms = { | |
| time: {value: 0}, | |
| speed: {value: 1}, | |
| size: {value: 20} | |
| } | |
| grid.material.onBeforeCompile = shader => { | |
| shader.uniforms.time = uniforms.time; | |
| shader.uniforms.speed = uniforms.speed; | |
| shader.uniforms.size = uniforms.size; | |
| shader.vertexShader = ` | |
| uniform float time; | |
| uniform float speed; | |
| uniform float size; | |
| attribute float moveable; | |
| ${shader.vertexShader} | |
| `.replace( | |
| `#include <begin_vertex>`, | |
| `#include <begin_vertex> | |
| if (floor(moveable + 0.1) > 0.5){ | |
| float start = size * -0.5; | |
| float zPos = mod( (position.z - start) + (time * speed), size) + start; | |
| transformed.z = zPos; | |
| } | |
| ` | |
| ); | |
| console.log(shader.vertexShader) | |
| } | |
| scene.add(grid); | |
| // palm | |
| let base = new THREE.Object3D(); | |
| let baseSpline = new THREE.CatmullRomCurve3([ | |
| new THREE.Vector2(), | |
| new THREE.Vector2(3, 0), | |
| new THREE.Vector2(2.5, -7), | |
| new THREE.Vector2(-4, -6), | |
| new THREE.Vector2(-4.8, 0) | |
| ], true, "catmullrom", 0.1); | |
| let baseG = new THREE.ExtrudeBufferGeometry(new THREE.Shape(baseSpline.getPoints(50)), {depth: 0.2, bevelEnabled: true, bevelThickness: 0.8, bevelSize: 0.2}); | |
| let baseObject = new THREE.Mesh(baseG, new THREE.MeshBasicMaterial({color: "magenta", wireframe: false, envMap: textureCube})); | |
| base.add(baseObject); | |
| scene.add(base); | |
| let phalanxes = []; | |
| let f1 = createFinger(new THREE.Object3D(), 0.8, false); // pinky | |
| let f2 = createFinger(new THREE.Object3D(), 0.95, false); // ring | |
| let f3 = createFinger(new THREE.Object3D(), 1, false); // middle | |
| let f4 = createFinger(new THREE.Object3D(), 0.95, false); // index | |
| let f5Base = new THREE.Object3D(); | |
| let f5 = createFinger(new THREE.Object3D(), 0.75, true); // thumb | |
| f5Base.add(f5); | |
| base.add(f1, f2, f3, f4, f5Base); | |
| f1.position.set( -4, 0.2, 0); | |
| f2.position.set( -2, 0.2, 0); | |
| f3.position.set( 0, 0.2, 0); | |
| f4.position.set( 2, 0.2, 0); | |
| f5Base.position.set( 3, -3, 0); | |
| f5Base.rotation.set( 0, 0, THREE.MathUtils.degToRad(-60)); | |
| f5Base.updateMatrixWorld(); | |
| let g = createPhalanxGeom(1, 3); | |
| let m = new THREE.MeshBasicMaterial({color: "aqua", wireframe: false, envMap: textureCube}); | |
| let o = new THREE.InstancedMesh(g, m, phalanxes.length); | |
| phalanxes.forEach( (ph, i) => { | |
| ph.updateMatrixWorld(); | |
| o.setMatrixAt(i, ph.matrixWorld); | |
| }) | |
| scene.add(o); | |
| window.addEventListener( 'resize', onWindowResize, false ); | |
| let t = new TWEEN.Tween({value: Math.PI * 0.075}) | |
| .to({value: Math.PI * 0.45}, 4000) | |
| .easing(TWEEN.Easing.Quadratic.InOut) | |
| .repeat(Infinity) | |
| .yoyo(true) | |
| .onUpdate(val => { | |
| phalanxes.forEach((ph, i) => { | |
| ph.rotation.x = val.value; | |
| ph.updateMatrixWorld(); | |
| o.setMatrixAt(i, ph.matrixWorld) | |
| }); | |
| o.instanceMatrix.needsUpdate = true; | |
| }); | |
| t.start(); | |
| let clock = new THREE.Clock(); | |
| renderer.setAnimationLoop(() => { | |
| let t = clock.getElapsedTime(); | |
| TWEEN.update(); | |
| uniforms.time.value = t; | |
| base.rotation.x = (Math.sin(t * 0.125) * 0.5 + 0.5) * -Math.PI * 0.5; | |
| base.rotation.y = -t * 0.125; | |
| renderer.render(scene, camera); | |
| }); | |
| function onWindowResize() { | |
| camera.aspect = innerWidth / innerHeight; | |
| camera.updateProjectionMatrix(); | |
| renderer.setSize( innerWidth, innerHeight ); | |
| } | |
| function createFinger(phalanx, scale, isThumb){ | |
| phalanxes.push(phalanx); | |
| let current = phalanx; | |
| for(let i = 0; i < (isThumb ? 1 : 2); i++){ | |
| let p = new THREE.Object3D(); | |
| p.position.y = 3; | |
| p.scale.setScalar(0.85); | |
| current.add(p); | |
| phalanxes.push(p); | |
| current = p; | |
| } | |
| phalanx.scale.setScalar(scale); | |
| return phalanx; | |
| } | |
| function createPhalanxGeom(R, L){ | |
| let r = R * 0.85; | |
| let R1 = R - r; | |
| let a = Math.asin(R1 / L); | |
| let path = new THREE.Path(); | |
| path.absarc(0, 0, R, Math.PI * 1.5, a); | |
| path.absarc(0, L, r, a, Math.PI * 0.5); | |
| let pts = path.getPoints(5); | |
| let g = new THREE.LatheBufferGeometry(pts); | |
| return g; | |
| } | |
| function generateCubeMap(){ | |
| let images = []; | |
| let c = document.createElement("canvas"); | |
| c.width = 4; | |
| c.height = c.width; | |
| let ctx = c.getContext("2d"); | |
| for(let i= 0; i < 6;i++){ | |
| ctx.fillStyle = "#fff"; | |
| ctx.fillRect(0, 0, c.width, c.height); | |
| for(let j = 0; j < (c.width * c.height) / 2; j++){ | |
| ctx.fillStyle = Math.random() < 0.5 ? "#f0f" : "#40f"; | |
| ctx.fillRect( | |
| Math.floor(Math.random() * c.width), | |
| Math.floor(Math.random() * c.height), | |
| 2, | |
| 1 | |
| ); | |
| } | |
| images.push(c.toDataURL()); | |
| } | |
| let cm = new THREE.CubeTextureLoader().load(images); | |
| console.log(cm); | |
| return cm; | |
| } | |
| </script> |