3d-website/3d.js
2026-01-29 20:18:34 -07:00

259 lines
6.6 KiB
JavaScript

import * as THREE from "three";
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x1a1a2e);
const camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000,
);
// Connect to the canvas element instead of appending to body
const renderer = new THREE.WebGLRenderer({
canvas: document.querySelector("#bg"),
antialias: true,
});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(window.devicePixelRatio);
// Lights
const ambientLight = new THREE.AmbientLight(0x404040, 0.5);
scene.add(ambientLight);
const mainLight = new THREE.DirectionalLight(0xffffff, 1);
mainLight.position.set(5, 5, 5);
scene.add(mainLight);
const fillLight = new THREE.DirectionalLight(0x4fc3f7, 0.3);
fillLight.position.set(-5, 0, -5);
scene.add(fillLight);
const protogen = new THREE.Group();
// Materials
const bodyMaterial = new THREE.MeshStandardMaterial({
color: 0x2d2d2d,
metalness: 0.7,
roughness: 0.3,
});
const accentMaterial = new THREE.MeshStandardMaterial({
color: 0x1a1a1a,
metalness: 0.8,
roughness: 0.2,
});
const glowMaterial = new THREE.MeshStandardMaterial({
color: 0x00ffff,
emissive: 0x00ffff,
emissiveIntensity: 0.8,
metalness: 0.3,
roughness: 0.4,
});
const visorMaterial = new THREE.MeshStandardMaterial({
color: 0x001a1a,
emissive: 0x003333,
emissiveIntensity: 0.3,
metalness: 0.9,
roughness: 0.1,
transparent: true,
opacity: 0.9,
});
// HEAD
const headGeometry = new THREE.BoxGeometry(2, 1.8, 2);
const head = new THREE.Mesh(headGeometry, bodyMaterial);
head.position.y = 0;
protogen.add(head);
const snoutGeometry = new THREE.BoxGeometry(1.4, 0.8, 1.2);
const snout = new THREE.Mesh(snoutGeometry, bodyMaterial);
snout.position.set(0, -0.3, 1.2);
protogen.add(snout);
// Snout tip
const snoutTipGeometry = new THREE.BoxGeometry(1.0, 0.5, 0.4);
const snoutTip = new THREE.Mesh(snoutTipGeometry, bodyMaterial);
snoutTip.position.set(0, -0.35, 1.9);
protogen.add(snoutTip);
// VISOR
const visorGeometry = new THREE.BoxGeometry(1.8, 1.2, 0.1);
const visor = new THREE.Mesh(visorGeometry, visorMaterial);
visor.position.set(0, 0.1, 1.05);
protogen.add(visor);
// Visor frame
const visorFrameShape = new THREE.Shape();
visorFrameShape.moveTo(-1, -0.7);
visorFrameShape.lineTo(1, -0.7);
visorFrameShape.lineTo(1, 0.7);
visorFrameShape.lineTo(-1, 0.7);
visorFrameShape.lineTo(-1, -0.7);
const visorHole = new THREE.Path();
visorHole.moveTo(-0.85, -0.55);
visorHole.lineTo(0.85, -0.55);
visorHole.lineTo(0.85, 0.55);
visorHole.lineTo(-0.85, 0.55);
visorHole.lineTo(-0.85, -0.55);
visorFrameShape.holes.push(visorHole);
const visorFrameGeometry = new THREE.ExtrudeGeometry(visorFrameShape, {
depth: 0.15,
bevelEnabled: false,
});
const visorFrame = new THREE.Mesh(visorFrameGeometry, accentMaterial);
visorFrame.position.set(0, 0.1, 1.0);
protogen.add(visorFrame);
// VISOR DISPLAY
const eyeGroup = new THREE.Group();
// Left eye
const leftEyeShape = new THREE.Shape();
leftEyeShape.moveTo(-0.5, 0);
leftEyeShape.lineTo(-0.2, 0.25);
leftEyeShape.lineTo(-0.2, 0.15);
leftEyeShape.lineTo(-0.35, 0);
leftEyeShape.lineTo(-0.2, -0.15);
leftEyeShape.lineTo(-0.2, -0.25);
leftEyeShape.lineTo(-0.5, 0);
const leftEyeGeometry = new THREE.ShapeGeometry(leftEyeShape);
const leftEye = new THREE.Mesh(leftEyeGeometry, glowMaterial);
leftEye.position.set(-0.25, 0.15, 1.12);
eyeGroup.add(leftEye);
// Right eye
const rightEyeShape = new THREE.Shape();
rightEyeShape.moveTo(0.5, 0);
rightEyeShape.lineTo(0.2, 0.25);
rightEyeShape.lineTo(0.2, 0.15);
rightEyeShape.lineTo(0.35, 0);
rightEyeShape.lineTo(0.2, -0.15);
rightEyeShape.lineTo(0.2, -0.25);
rightEyeShape.lineTo(0.5, 0);
const rightEyeGeometry = new THREE.ShapeGeometry(rightEyeShape);
const rightEye = new THREE.Mesh(rightEyeGeometry, glowMaterial);
rightEye.position.set(0.25, 0.15, 1.12);
eyeGroup.add(rightEye);
protogen.add(eyeGroup);
// EARS
function createEar(isLeft) {
const earGroup = new THREE.Group();
// Main ear
const earShape = new THREE.Shape();
earShape.moveTo(0, 0);
earShape.lineTo(0.4, 0);
earShape.lineTo(0.2, 1.0);
earShape.lineTo(0, 0);
const earGeometry = new THREE.ExtrudeGeometry(earShape, {
depth: 0.3,
bevelEnabled: true,
bevelThickness: 0.05,
bevelSize: 0.05,
});
const ear = new THREE.Mesh(earGeometry, bodyMaterial);
earGroup.add(ear);
// Inner ear glow
const innerEarShape = new THREE.Shape();
innerEarShape.moveTo(0.1, 0.15);
innerEarShape.lineTo(0.3, 0.15);
innerEarShape.lineTo(0.2, 0.7);
innerEarShape.lineTo(0.1, 0.15);
const innerEarGeometry = new THREE.ShapeGeometry(innerEarShape);
const innerEar = new THREE.Mesh(innerEarGeometry, glowMaterial);
innerEar.position.z = 0.31;
earGroup.add(innerEar);
const xPos = isLeft ? -0.9 : 0.5;
earGroup.position.set(xPos, 0.7, -0.3);
earGroup.rotation.x = -0.3;
earGroup.rotation.z = isLeft ? 0.2 : -0.2;
return earGroup;
}
protogen.add(createEar(true));
protogen.add(createEar(false));
// NECK
const neckGeometry = new THREE.CylinderGeometry(0.6, 0.7, 0.8, 8);
const neck = new THREE.Mesh(neckGeometry, bodyMaterial);
neck.position.set(0, -1.2, 0);
protogen.add(neck);
for (let i = 0; i < 3; i++) {
const ringGeometry = new THREE.TorusGeometry(0.65, 0.03, 8, 32);
const ring = new THREE.Mesh(ringGeometry, glowMaterial);
ring.position.set(0, -0.95 - i * 0.2, 0);
ring.rotation.x = Math.PI / 2;
protogen.add(ring);
}
// CHEEK ACCENTS
const cheekGeometry = new THREE.BoxGeometry(0.1, 0.4, 0.6);
const leftCheek = new THREE.Mesh(cheekGeometry, glowMaterial);
leftCheek.position.set(-1.05, 0, 0.5);
protogen.add(leftCheek);
const rightCheek = new THREE.Mesh(cheekGeometry, glowMaterial);
rightCheek.position.set(1.05, 0, 0.5);
protogen.add(rightCheek);
scene.add(protogen);
// Set initial camera position
camera.position.set(0, 0, 5);
// Handle window resize
window.addEventListener("resize", () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
// Scroll animation function
function moveCamera() {
const t = document.body.getBoundingClientRect().top;
// Rotate protogen slightly on scroll
protogen.rotation.y = t * -0.002;
protogen.rotation.x = t * -0.0005;
// Move camera on scroll
camera.position.z = 5 + t * -0.005;
}
document.body.onscroll = moveCamera;
moveCamera();
let time = 0;
function animate() {
requestAnimationFrame(animate);
time += 0.016;
// Gentle floating animation
protogen.position.y = Math.sin(time * 0.5) * 0.1;
// Pulsing glow effect
const pulse = 0.6 + Math.sin(time * 2) * 0.3;
glowMaterial.emissiveIntensity = pulse;
renderer.render(scene, camera);
}
animate();