Add packaging design and 3D render

This commit is contained in:
OpenClaw
2026-03-17 16:59:54 +08:00
parent 53e0bfa4e2
commit c1db697fbe
2 changed files with 2103 additions and 0 deletions

857
3d.html Normal file
View File

@@ -0,0 +1,857 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>卡特光醒狮系列 - 包装3D渲染展示</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/controls/OrbitControls.js"></script>
<style>
@import url('https://fonts.googleapis.com/css2?family=Noto+Serif+SC:wght@400;600;700&display=swap');
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Noto Serif SC', serif;
background: linear-gradient(135deg, #0D0D0D 0%, #1a1a1a 50%, #0D0D0D 100%);
color: #F5F5F0;
overflow: hidden;
}
#canvas-container {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1;
}
.ui-layer {
position: absolute;
z-index: 10;
pointer-events: none;
}
.ui-interactive {
pointer-events: auto;
}
.header {
top: 0;
left: 0;
right: 0;
padding: 24px 40px;
background: linear-gradient(180deg, rgba(13,13,13,0.95) 0%, rgba(13,13,13,0.7) 60%, transparent 100%);
}
.logo {
font-size: 14px;
color: #D4A853;
letter-spacing: 4px;
margin-bottom: 8px;
}
.title {
font-size: 32px;
font-weight: 700;
background: linear-gradient(135deg, #D4A853 0%, #F5F5F0 50%, #D4A853 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.subtitle {
font-size: 14px;
color: rgba(245,245,240,0.6);
margin-top: 4px;
letter-spacing: 2px;
}
.product-selector {
bottom: 40px;
left: 50%;
transform: translateX(-50%);
display: flex;
gap: 16px;
padding: 20px;
background: rgba(13,13,13,0.85);
border: 1px solid rgba(212,168,83,0.3);
border-radius: 16px;
backdrop-filter: blur(20px);
}
.product-btn {
width: 80px;
height: 100px;
border: 2px solid transparent;
border-radius: 12px;
cursor: pointer;
transition: all 0.3s ease;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 8px;
background: rgba(255,255,255,0.05);
position: relative;
overflow: hidden;
}
.product-btn::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
opacity: 0.1;
transition: opacity 0.3s ease;
}
.product-btn:hover {
transform: translateY(-4px);
border-color: rgba(212,168,83,0.5);
background: rgba(255,255,255,0.1);
}
.product-btn.active {
border-color: #D4A853;
background: rgba(212,168,83,0.15);
}
.product-btn.active::before {
opacity: 0.2;
}
.product-icon {
width: 40px;
height: 40px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 20px;
}
.product-name {
font-size: 12px;
font-weight: 600;
text-align: center;
}
.view-controls {
right: 40px;
top: 50%;
transform: translateY(-50%);
display: flex;
flex-direction: column;
gap: 12px;
}
.view-btn {
width: 56px;
height: 56px;
border: 1px solid rgba(212,168,83,0.3);
border-radius: 12px;
background: rgba(13,13,13,0.8);
color: #F5F5F0;
cursor: pointer;
transition: all 0.3s ease;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 4px;
font-size: 10px;
}
.view-btn:hover {
border-color: #D4A853;
background: rgba(212,168,83,0.15);
}
.view-btn.active {
background: linear-gradient(135deg, rgba(212,168,83,0.3), rgba(212,168,83,0.1));
border-color: #D4A853;
}
.view-btn svg {
width: 20px;
height: 20px;
stroke: currentColor;
fill: none;
stroke-width: 1.5;
}
.action-buttons {
top: 40px;
right: 40px;
display: flex;
gap: 12px;
}
.action-btn {
padding: 12px 24px;
border: 1px solid rgba(212,168,83,0.4);
border-radius: 8px;
background: rgba(13,13,13,0.8);
color: #D4A853;
font-size: 13px;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
display: flex;
align-items: center;
gap: 8px;
backdrop-filter: blur(10px);
}
.action-btn:hover {
background: rgba(212,168,83,0.2);
border-color: #D4A853;
}
.info-panel {
bottom: 180px;
left: 40px;
max-width: 280px;
padding: 24px;
background: rgba(13,13,13,0.85);
border: 1px solid rgba(212,168,83,0.2);
border-radius: 16px;
backdrop-filter: blur(20px);
}
.info-title {
font-size: 18px;
font-weight: 700;
color: #D4A853;
margin-bottom: 12px;
}
.info-desc {
font-size: 13px;
color: rgba(245,245,240,0.7);
line-height: 1.6;
}
.color-indicator {
display: flex;
gap: 8px;
margin-top: 16px;
}
.color-dot {
width: 24px;
height: 24px;
border-radius: 50%;
border: 2px solid rgba(255,255,255,0.2);
}
.loading-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: #0D0D0D;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
z-index: 1000;
transition: opacity 0.5s ease;
}
.loading-spinner {
width: 60px;
height: 60px;
border: 2px solid rgba(212,168,83,0.2);
border-top-color: #D4A853;
border-radius: 50%;
animation: spin 1s linear infinite;
}
.loading-text {
margin-top: 20px;
color: #D4A853;
font-size: 14px;
letter-spacing: 2px;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
.hidden {
opacity: 0;
pointer-events: none;
}
.lens-glow {
filter: drop-shadow(0 0 20px currentColor);
}
</style>
</head>
<body>
<!-- Loading Overlay -->
<div class="loading-overlay" id="loading">
<div class="loading-spinner"></div>
<div class="loading-text">正在加载3D渲染...</div>
</div>
<!-- 3D Canvas -->
<div id="canvas-container"></div>
<!-- Header -->
<div class="ui-layer header">
<div class="logo">KART LIGHTING</div>
<div class="title">醒狮系列透镜</div>
<div class="subtitle">XINGSHI LENS COLLECTION</div>
</div>
<!-- Action Buttons -->
<div class="ui-layer action-buttons ui-interactive">
<button class="action-btn" onclick="toggleBoxOpen()">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M5 15l7-7 7 7"/>
</svg>
<span id="open-btn-text">打开包装</span>
</button>
<button class="action-btn" onclick="takeScreenshot()">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<rect x="3" y="6" width="18" height="12" rx="2"/>
<circle cx="12" cy="12" r="3"/>
</svg>
截图
</button>
</div>
<!-- Info Panel -->
<div class="ui-layer info-panel" id="info-panel">
<div class="info-title" id="product-title">醒狮·瞳</div>
<div class="info-desc" id="product-desc">
狮头设计,象征洞察与智慧。帝王金配色彰显尊贵气质,为驾驶者带来清晰明亮的视野体验。
</div>
<div class="color-indicator" id="color-indicator">
<div class="color-dot" style="background: #FFD700;"></div>
</div>
</div>
<!-- View Controls -->
<div class="ui-layer view-controls ui-interactive">
<button class="view-btn active" onclick="setView('front')" title="正面视角">
<svg viewBox="0 0 24 24">
<rect x="3" y="3" width="18" height="18" rx="2"/>
<path d="M12 8v8M8 12h8"/>
</svg>
<span>正面</span>
</button>
<button class="view-btn" onclick="setView('angle')" title="45度视角">
<svg viewBox="0 0 24 24">
<path d="M3 7l9-4 9 4v10l-9 4-9-4V7z"/>
<path d="M12 3v18"/>
</svg>
<span>45°</span>
</button>
<button class="view-btn" onclick="setView('top')" title="顶面视角">
<svg viewBox="0 0 24 24">
<rect x="4" y="4" width="16" height="16" rx="2"/>
<circle cx="12" cy="12" r="4"/>
</svg>
<span>顶面</span>
</button>
<button class="view-btn" onclick="setView('free')" title="自由视角">
<svg viewBox="0 0 24 24">
<circle cx="12" cy="12" r="3"/>
<path d="M12 2v4M12 18v4M2 12h4M18 12h4"/>
</svg>
<span>自由</span>
</button>
</div>
<!-- Product Selector -->
<div class="ui-layer product-selector ui-interactive">
<div class="product-btn active" onclick="selectProduct(0)" data-product="0">
<div class="product-icon" style="background: linear-gradient(135deg, #FFD700, #D4A853); color: #0D0D0D;"></div>
<div class="product-name">醒狮·瞳</div>
</div>
<div class="product-btn" onclick="selectProduct(1)" data-product="1">
<div class="product-icon" style="background: linear-gradient(135deg, #FF6B35, #C41E3A); color: #fff;"></div>
<div class="product-name">醒狮·威</div>
</div>
<div class="product-btn" onclick="selectProduct(2)" data-product="2">
<div class="product-icon" style="background: linear-gradient(135deg, #8B7355, #5C4A3A); color: #fff;"></div>
<div class="product-name">醒狮·鼓</div>
</div>
<div class="product-btn" onclick="selectProduct(3)" data-product="3">
<div class="product-icon" style="background: linear-gradient(135deg, #6B4EE6, #9B7BF7); color: #fff;"></div>
<div class="product-name">醒狮·潮</div>
</div>
</div>
<script>
// Product Data
const products = [
{
name: '醒狮·瞳',
subtitle: 'XINGSHI TONG',
color: '#FFD700',
accentColor: '#D4A853',
description: '狮头设计,象征洞察与智慧。帝王金配色彰显尊贵气质,为驾驶者带来清晰明亮的视野体验。',
pattern: 'lion_head'
},
{
name: '醒狮·威',
subtitle: 'XINGSHI WEI',
color: '#FF6B35',
accentColor: '#C41E3A',
description: '狮尾设计,象征力量与威严。烈焰橙配色展现澎湃动力,为车辆注入强劲光芒。',
pattern: 'lion_tail'
},
{
name: '醒狮·鼓',
subtitle: 'XINGSHI GU',
color: '#8B7355',
accentColor: '#D4A853',
description: '鼓手设计,象征节奏与韵律。古铜褐配色传递沉稳底蕴,为行车带来和谐节奏。',
pattern: 'drummer'
},
{
name: '醒狮·潮',
subtitle: 'XINGSHI CHAO',
color: '#6B4EE6',
accentColor: '#9B7BF7',
description: '少年设计,象征活力与创新。电光紫配色诠释年轻态度,为爱车增添时尚魅力。',
pattern: 'youth'
}
];
let currentProduct = 0;
let isBoxOpen = false;
let scene, camera, renderer, controls;
let boxGroup, lidGroup, lensMesh;
let animationId;
// Initialize Three.js
function init() {
const container = document.getElementById('canvas-container');
// Scene
scene = new THREE.Scene();
scene.fog = new THREE.FogExp2(0x0D0D0D, 0.02);
// Camera
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(0, 0, 12);
// Renderer
renderer = new THREE.WebGLRenderer({
antialias: true,
alpha: true,
preserveDrawingBuffer: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
renderer.toneMapping = THREE.ACESFilmicToneMapping;
renderer.toneMappingExposure = 1.2;
container.appendChild(renderer.domElement);
// Controls
controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.dampingFactor = 0.05;
controls.minDistance = 5;
controls.maxDistance = 20;
controls.maxPolarAngle = Math.PI * 0.8;
// Lighting
setupLighting();
// Create Box
createPackagingBox();
// Start Animation
animate();
// Hide Loading
setTimeout(() => {
document.getElementById('loading').classList.add('hidden');
}, 1000);
// Resize Handler
window.addEventListener('resize', onWindowResize);
}
function setupLighting() {
// Ambient
const ambientLight = new THREE.AmbientLight(0xffffff, 0.3);
scene.add(ambientLight);
// Key Light (warm gold)
const keyLight = new THREE.SpotLight(0xD4A853, 1.5);
keyLight.position.set(8, 10, 8);
keyLight.castShadow = true;
keyLight.shadow.mapSize.width = 2048;
keyLight.shadow.mapSize.height = 2048;
keyLight.shadow.bias = -0.0001;
scene.add(keyLight);
// Fill Light (cool)
const fillLight = new THREE.DirectionalLight(0x6B4EE6, 0.4);
fillLight.position.set(-8, 5, 5);
scene.add(fillLight);
// Rim Light
const rimLight = new THREE.SpotLight(0xFFD700, 0.8);
rimLight.position.set(0, 8, -8);
scene.add(rimLight);
// Bottom reflection
const bottomLight = new THREE.DirectionalLight(0xffffff, 0.2);
bottomLight.position.set(0, -5, 0);
scene.add(bottomLight);
}
function createPackagingBox() {
if (boxGroup) scene.remove(boxGroup);
boxGroup = new THREE.Group();
const product = products[currentProduct];
const boxWidth = 5.3;
const boxHeight = 4.1;
const boxDepth = 2.0;
// Black card paper material
const boxMaterial = new THREE.MeshStandardMaterial({
color: 0x0D0D0D,
roughness: 0.9,
metalness: 0.1,
bumpScale: 0.02
});
// Gold foil material for patterns
const goldMaterial = new THREE.MeshStandardMaterial({
color: 0xD4A853,
roughness: 0.2,
metalness: 0.9,
emissive: 0xD4A853,
emissiveIntensity: 0.1
});
// Product accent material
const accentMaterial = new THREE.MeshStandardMaterial({
color: new THREE.Color(product.color),
roughness: 0.3,
metalness: 0.7,
emissive: new THREE.Color(product.color),
emissiveIntensity: 0.2
});
// Main box body
const bodyGeometry = new THREE.BoxGeometry(boxWidth, boxHeight, boxDepth);
const bodyMesh = new THREE.Mesh(bodyGeometry, boxMaterial);
bodyMesh.castShadow = true;
bodyMesh.receiveShadow = true;
boxGroup.add(bodyMesh);
// Front face design - decorative frame
const frameGeometry = new THREE.PlaneGeometry(boxWidth * 0.85, boxHeight * 0.85);
const frameMaterial = new THREE.MeshStandardMaterial({
color: 0xD4A853,
roughness: 0.3,
metalness: 0.8,
transparent: true,
opacity: 0.9,
side: THREE.DoubleSide
});
const frameMesh = new THREE.Mesh(frameGeometry, frameMaterial);
frameMesh.position.set(0, 0, boxDepth/2 + 0.01);
boxGroup.add(frameMesh);
// Inner frame
const innerFrameGeometry = new THREE.PlaneGeometry(boxWidth * 0.75, boxHeight * 0.75);
const innerFrameMesh = new THREE.Mesh(innerFrameGeometry, boxMaterial);
innerFrameMesh.position.set(0, 0, boxDepth/2 + 0.02);
boxGroup.add(innerFrameMesh);
// Lion pattern area
const patternGeometry = new THREE.PlaneGeometry(boxWidth * 0.5, boxWidth * 0.5);
const patternMaterial = new THREE.MeshStandardMaterial({
color: new THREE.Color(product.color),
roughness: 0.4,
metalness: 0.6,
emissive: new THREE.Color(product.color),
emissiveIntensity: 0.15
});
const patternMesh = new THREE.Mesh(patternGeometry, patternMaterial);
patternMesh.position.set(0, 0.3, boxDepth/2 + 0.03);
boxGroup.add(patternMesh);
// Product name text area
const nameBgGeometry = new THREE.PlaneGeometry(boxWidth * 0.6, 0.8);
const nameBgMaterial = new THREE.MeshStandardMaterial({
color: 0x0D0D0D,
roughness: 0.9,
metalness: 0.1
});
const nameBgMesh = new THREE.Mesh(nameBgGeometry, nameBgMaterial);
nameBgMesh.position.set(0, -1.2, boxDepth/2 + 0.03);
boxGroup.add(nameBgMesh);
// Brand logo strip
const logoStripGeometry = new THREE.PlaneGeometry(boxWidth, 0.4);
const logoStripMaterial = new THREE.MeshStandardMaterial({
color: 0xD4A853,
roughness: 0.2,
metalness: 0.9
});
const logoStripMesh = new THREE.Mesh(logoStripGeometry, logoStripMaterial);
logoStripMesh.position.set(0, 1.6, boxDepth/2 + 0.03);
boxGroup.add(logoStripMesh);
// Top surface design
const topDesignGeometry = new THREE.PlaneGeometry(boxWidth * 0.7, boxDepth * 0.7);
const topDesignMaterial = new THREE.MeshStandardMaterial({
color: 0xD4A853,
roughness: 0.3,
metalness: 0.8
});
const topDesignMesh = new THREE.Mesh(topDesignGeometry, topDesignMaterial);
topDesignMesh.rotation.x = -Math.PI / 2;
topDesignMesh.position.set(0, boxHeight/2 + 0.01, 0);
boxGroup.add(topDesignMesh);
// Side decorations
const sideDecoGeometry = new THREE.PlaneGeometry(boxDepth * 0.6, boxHeight * 0.6);
const sideDecoMaterial = new THREE.MeshStandardMaterial({
color: new THREE.Color(product.accentColor),
roughness: 0.4,
metalness: 0.6
});
// Left side
const leftDeco = new THREE.Mesh(sideDecoGeometry, sideDecoMaterial);
leftDeco.rotation.y = -Math.PI / 2;
leftDeco.position.set(-boxWidth/2 - 0.01, 0, 0);
boxGroup.add(leftDeco);
// Right side
const rightDeco = new THREE.Mesh(sideDecoGeometry, sideDecoMaterial);
rightDeco.rotation.y = Math.PI / 2;
rightDeco.position.set(boxWidth/2 + 0.01, 0, 0);
boxGroup.add(rightDeco);
// Lid group (for opening animation)
lidGroup = new THREE.Group();
lidGroup.position.set(0, boxHeight/2, -boxDepth/2);
// Lid mesh
const lidGeometry = new THREE.BoxGeometry(boxWidth, 0.15, boxDepth);
const lidMesh = new THREE.Mesh(lidGeometry, boxMaterial);
lidMesh.position.set(0, 0.075, boxDepth/2);
lidMesh.castShadow = true;
lidGroup.add(lidMesh);
// Lid top design
const lidTopGeometry = new THREE.PlaneGeometry(boxWidth * 0.8, boxDepth * 0.8);
const lidTopMesh = new THREE.Mesh(lidTopGeometry, goldMaterial);
lidTopMesh.rotation.x = -Math.PI / 2;
lidTopMesh.position.set(0, 0.16, boxDepth/2);
lidGroup.add(lidTopMesh);
boxGroup.add(lidGroup);
// Lens product (inside)
createLensProduct();
scene.add(boxGroup);
}
function createLensProduct() {
const product = products[currentProduct];
// Lens body
const lensGeometry = new THREE.CylinderGeometry(1.2, 1.2, 0.4, 32);
const lensMaterial = new THREE.MeshPhysicalMaterial({
color: 0x1a1a1a,
metalness: 0.8,
roughness: 0.2,
clearcoat: 1.0,
clearcoatRoughness: 0.1
});
lensMesh = new THREE.Mesh(lensGeometry, lensMaterial);
lensMesh.position.set(0, -0.5, 0);
lensMesh.rotation.x = Math.PI / 2;
lensMesh.visible = false;
boxGroup.add(lensMesh);
// Lens glass
const glassGeometry = new THREE.CylinderGeometry(1.0, 1.0, 0.1, 32);
const glassMaterial = new THREE.MeshPhysicalMaterial({
color: new THREE.Color(product.color),
metalness: 0.1,
roughness: 0.0,
transmission: 0.9,
thickness: 0.5,
emissive: new THREE.Color(product.color),
emissiveIntensity: 0.3
});
const glassMesh = new THREE.Mesh(glassGeometry, glassMaterial);
glassMesh.position.set(0, -0.5, 0.25);
glassMesh.rotation.x = Math.PI / 2;
glassMesh.visible = false;
glassMesh.name = 'lensGlass';
boxGroup.add(glassMesh);
// LED chips ring
const ledGeometry = new THREE.RingGeometry(0.6, 0.9, 32);
const ledMaterial = new THREE.MeshBasicMaterial({
color: new THREE.Color(product.color),
side: THREE.DoubleSide
});
const ledMesh = new THREE.Mesh(ledGeometry, ledMaterial);
ledMesh.position.set(0, -0.5, 0.26);
ledMesh.visible = false;
ledMesh.name = 'ledRing';
boxGroup.add(ledMesh);
}
function selectProduct(index) {
currentProduct = index;
// Update UI
document.querySelectorAll('.product-btn').forEach((btn, i) => {
btn.classList.toggle('active', i === index);
});
const product = products[index];
document.getElementById('product-title').textContent = product.name;
document.getElementById('product-desc').textContent = product.description;
document.getElementById('color-indicator').innerHTML =
`<div class="color-dot" style="background: ${product.color};"></div>`;
// Recreate box with new product
createPackagingBox();
// Maintain open state if was open
if (isBoxOpen) {
setTimeout(() => toggleBoxOpen(), 100);
}
}
function setView(view) {
// Update UI
document.querySelectorAll('.view-btn').forEach(btn => btn.classList.remove('active'));
event.target.closest('.view-btn').classList.add('active');
const duration = 1000;
const startPos = camera.position.clone();
let targetPos;
switch(view) {
case 'front':
targetPos = new THREE.Vector3(0, 0, 12);
break;
case 'angle':
targetPos = new THREE.Vector3(8, 6, 8);
break;
case 'top':
targetPos = new THREE.Vector3(0, 12, 0.1);
break;
case 'free':
targetPos = new THREE.Vector3(6, 4, 10);
break;
}
// Animate camera
const startTime = Date.now();
function animateCamera() {
const elapsed = Date.now() - startTime;
const progress = Math.min(elapsed / duration, 1);
const ease = 1 - Math.pow(1 - progress, 3);
camera.position.lerpVectors(startPos, targetPos, ease);
camera.lookAt(0, 0, 0);
if (progress < 1) {
requestAnimationFrame(animateCamera);
}
}
animateCamera();
}
function toggleBoxOpen() {
isBoxOpen = !isBoxOpen;
document.getElementById('open-btn-text').textContent = isBoxOpen ? '关闭包装' : '打开包装';
const targetRotation = isBoxOpen ? -Math.PI * 0.7 : 0;
const startRotation = lidGroup.rotation.x;
const duration = 800;
const startTime = Date.now();
// Show/hide lens
const lensParts = ['lensGlass', 'ledRing'];
lensParts.forEach(name => {
const part = boxGroup.getObjectByName(name);
if (part) part.visible = isBoxOpen;
});
if (lensMesh) lensMesh.visible = isBoxOpen;
function animateLid() {
const elapsed = Date.now() - startTime;
const progress = Math.min(elapsed / duration, 1);
const ease = 1 - Math.pow(1 - progress, 3);
lidGroup.rotation.x = startRotation + (targetRotation - startRotation) * ease;
if (progress < 1) {
requestAnimationFrame(animateLid);
}
}
animateLid();
}
function takeScreenshot() {
renderer.render(scene, camera);
const dataURL = renderer.domElement.toDataURL('image/png');
const link = document.createElement('a');
link.download = `醒狮包装_${products[currentProduct].name}_${new Date().getTime()}.png`;
link.href = dataURL;
link.click();
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
function animate() {
animationId = requestAnimationFrame(animate);
controls.update();
// Subtle floating animation
if (boxGroup) {
boxGroup.position.y = Math.sin(Date.now() * 0.001) * 0.1;
boxGroup.rotation.y += 0.001;
}
renderer.render(scene, camera);
}
// Initialize
init();
</script>
</body>
</html>

1246
index.html Normal file

File diff suppressed because it is too large Load Diff