|
<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> |