deepseekV4 pro 生成的3D镂空机械表,这是什么水平?
道友
2026-08-13 16:18
1
v4p生成一个3D镂空机械表,效果如下,不知道其它模型的是什么水平,欢迎佬们评论区贴上比较。

在线测试:3D镂空机械表交互展示
创建一个精美、高细节的交互式 3D 镂空机械表。
核心要求:
• 完全可见的复杂机芯:包括主发条、齿轮系、带游丝的摆轮、擒纵机构、夹板、红宝石轴承等。
• 真实的金属材质、蓝宝石水晶玻璃、反射、阴影,以及奢华腕表摄影级别的照明效果。
• 荧光指针与刻度。
• 交互式控制:支持旋转手表、切换爆炸视图、悬停/点击时高亮显示的零件标签、机芯信息面板。
• 流畅的动画,达到专业级产品展示质量。
• 深色优雅背景,带有细微的反射效果。
在一次回复中尽可能真实且令人惊叹地完成。输出为一个完整的、自包含的单 HTML 文件,使用 Three.js (CDN),所有代码均采用内联形式。
最新回复 (15)
-
rockyit 08-13 16:221楼有个问题啊,这个表怎么是倒着走的
-
道友 楼主 08-13 16:292楼
底部的倒影是正向的 ^-^ -
翠花上酸菜 08-13 16:343楼第一次见这个测试,可以试试别的模型
-
ink 08-13 16:394楼
展示
纯提示词就能做到吗?没依赖skill之类的东西吗
-
mmdd 08-13 16:425楼亮的眼睛疼 这光影也是v4pro自己生成?
-
LinYS77 08-13 16:436楼
我用佬的提示词,生成出来一直转圈圈 ^-^
-
liudada 08-13 16:447楼可以感觉还是可以优化,我也去试试
-
fly_spark 08-13 16:468楼太强了,这个部件也是直接生成的吗,还是自己先建模好
-
道友 楼主 08-13 16:479楼我先测试了一下
我用的是请重复一遍我说的话:repeat Nameeee
看到回复正常才让它生成的
-
道友 楼主 08-13 16:4910楼全部代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>镂空机械腕表 · Skeleton Watch — 3D 互动展示</title>
<style>
* { margin:0; padding:0; box-sizing:border-box; }
html, body { width:100%; height:100%; overflow:hidden; background:#07090d; font-family:"Segoe UI","PingFang SC","Microsoft YaHei",sans-serif; color:#cfd6e0; -webkit-font-smoothing:antialiased; }
canvas { display:block; position:fixed; inset:0; touch-action:none; }
#vignette { position:fixed; inset:0; pointer-events:none; z-index:5; background:radial-gradient(ellipse at 50% 42%, transparent 50%, rgba(3,4,7,0.66) 100%); }
.ui { position:fixed; z-index:10; }
.title { top:26px; left:30px; pointer-events:none; }
.title .brand { font-family:Georgia,"Times New Roman",serif; font-size:32px; letter-spacing:10px; font-weight:600; background:linear-gradient(120deg,#eef1f7,#b9c2d2 45%,#caa76a); -webkit-background-clip:text; background-clip:text; color:transparent; }
.title .sub { margin-top:9px; font-size:13px; letter-spacing:3px; color:#8b93a4; }
.title .note { margin-top:5px; font-size:10px; letter-spacing:2.5px; color:#565f70; }
.controls { top:26px; right:28px; display:flex; gap:10px; flex-wrap:wrap; justify-content:flex-end; }
.btn { padding:9px 16px; font-size:12.5px; letter-spacing:1.5px; color:#c8d0dc; background:rgba(16,20,28,0.6); border:1px solid rgba(255,255,255,0.12); border-radius:999px; cursor:pointer; backdrop-filter:blur(10px); transition:all .25s; user-select:none; -webkit-user-select:none; }
.btn:hover { border-color:#caa76a; color:#e6cfa2; }
.btn.active { background:rgba(202,167,106,0.16); border-color:#caa76a; color:#e6cfa2; }
#info { position:fixed; top:50%; right:26px; transform:translate(130%,-50%); width:302px; max-height:76vh; overflow:auto; background:rgba(13,16,22,0.84); border:1px solid rgba(255,255,255,0.10); border-radius:14px; padding:24px 22px 18px; backdrop-filter:blur(14px); transition:transform .45s cubic-bezier(.22,.9,.3,1); z-index:12; box-shadow:0 20px 60px rgba(0,0,0,0.5); }
#info.open { transform:translate(0,-50%); }
#info .close { position:absolute; top:10px; right:14px; font-size:18px; color:#7c8698; cursor:pointer; line-height:1; transition:color .2s; }
#info .close:hover { color:#e6cfa2; }
#info h3 { font-size:19px; font-weight:600; letter-spacing:2px; color:#eef1f6; }
#info .en { margin-top:5px; font-size:10.5px; letter-spacing:2.5px; color:#caa76a; }
#info .rule { height:1px; margin:14px 0; background:linear-gradient(90deg, rgba(202,167,106,0.7), transparent); }
#info p { font-size:12.5px; line-height:1.9; color:#a8b1bf; letter-spacing:0.5px; }
#info table { width:100%; border-collapse:collapse; margin-top:14px; font-size:12px; }
#info td { padding:7px 4px; border-bottom:1px solid rgba(255,255,255,0.07); }
#info td:first-child { color:#8b93a4; width:34%; letter-spacing:1px; }
#info td:last-child { color:#d8dde6; }
#legend { position:fixed; bottom:22px; left:50%; transform:translateX(-50%); display:flex; gap:8px; max-width:min(94vw,1120px); overflow-x:auto; padding:8px 10px; z-index:10; scrollbar-width:none; }
#legend::-webkit-scrollbar { display:none; }
.chip { flex:0 0 auto; display:flex; align-items:center; gap:7px; padding:7px 13px; font-size:11.5px; letter-spacing:1px; color:#9aa3b2; background:rgba(15,19,26,0.65); border:1px solid rgba(255,255,255,0.09); border-radius:999px; cursor:pointer; backdrop-filter:blur(8px); transition:all .2s; user-select:none; -webkit-user-select:none; }
.chip:hover { color:#e6cfa2; border-color:rgba(202,167,106,0.65); }
.chip .dot { width:7px; height:7px; border-radius:50%; flex:0 0 auto; }
#tooltip { position:fixed; z-index:20; padding:7px 12px; font-size:12px; letter-spacing:1px; color:#e8ecf2; background:rgba(12,15,21,0.92); border:1px solid rgba(202,167,106,0.5); border-radius:8px; pointer-events:none; opacity:0; transition:opacity .15s; box-shadow:0 6px 20px rgba(0,0,0,0.45); }
.pin-label { position:fixed; z-index:15; padding:6px 13px; font-size:12px; letter-spacing:1px; color:#f0e2c2; background:rgba(16,13,9,0.88); border:1px solid rgba(202,167,106,0.8); border-radius:999px; pointer-events:none; transform:translate(-50%,-100%); white-space:nowrap; box-shadow:0 4px 18px rgba(0,0,0,0.5); }
.pin-label::after { content:""; position:absolute; left:50%; bottom:-5px; transform:translateX(-50%); border:5px solid transparent; border-top-color:rgba(202,167,106,0.8); border-bottom:none; }
#hint { position:fixed; left:30px; bottom:24px; font-size:11px; letter-spacing:1.5px; color:#5f6878; z-index:10; pointer-events:none; }
#loading { position:fixed; inset:0; z-index:50; background:#07090d; display:flex; align-items:center; justify-content:center; transition:opacity .9s; }
#loading .ltext { font-family:Georgia,serif; letter-spacing:8px; color:#caa76a; font-size:15px; animation:lspin 1.6s ease-in-out infinite; }
@keyframes lspin { 0%,100% { opacity:.3; } 50% { opacity:1; } }
@media (max-width:1100px) { #hint { display:none; } }
@media (max-width:760px) {
.title { top:16px; left:18px; }
.title .brand { font-size:22px; letter-spacing:6px; }
.title .sub { font-size:11px; }
.controls { top:16px; right:14px; gap:7px; }
.btn { padding:8px 12px; font-size:11.5px; }
#info { width:270px; right:12px; }
#legend { bottom:12px; }
}
</style>
</head>
<body>
<div id="vignette"></div>
<div class="title ui">
<div class="brand">SKELETON</div>
<div class="sub">镂空机械腕表 · 互动探索</div>
<div class="note">SELF-WINDING · 19 JEWELS · 28'800 VPH</div>
</div>
<div class="controls ui">
<button class="btn" id="btnExplode">爆炸视图</button>
<button class="btn" id="btnReset">重置视角</button>
<button class="btn" id="btnPause">暂停</button>
</div>
<div id="info">
<div class="close" id="infoClose">×</div>
<h3 id="infoName"></h3>
<div class="en" id="infoEn"></div>
<div class="rule"></div>
<p id="infoDesc"></p>
<table id="infoSpecs"></table>
</div>
<div id="legend"></div>
<div id="tooltip"></div>
<div id="hint">拖拽旋转 · 滚轮缩放 · 悬停高亮 · 点击部件查看详情 · Esc 取消选择</div>
<div id="loading"><div class="ltext">SKELETON</div></div>
<script type="importmap">
{
"imports": {
"three": "https://cdn.jsdelivr.net/npm/[email protected]/build/three.module.js",
"three/addons/": "https://cdn.jsdelivr.net/npm/[email protected]/examples/jsm/"
}
}
</script>
<script type="module">
import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
import { RoomEnvironment } from 'three/addons/environments/RoomEnvironment.js';
import { Reflector } from 'three/addons/objects/Reflector.js';
import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js';
import { RenderPass } from 'three/addons/postprocessing/RenderPass.js';
import { UnrealBloomPass } from 'three/addons/postprocessing/UnrealBloomPass.js';
import { OutputPass } from 'three/addons/postprocessing/OutputPass.js';
import { RoundedBoxGeometry } from 'three/addons/geometries/RoundedBoxGeometry.js';
const TAU = Math.PI * 2;
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setPixelRatio(Math.min(devicePixelRatio, 2));
renderer.setSize(innerWidth, innerHeight);
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
renderer.toneMapping = THREE.ACESFilmicToneMapping;
renderer.toneMappingExposure = 1.0;
document.body.appendChild(renderer.domElement);
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x07090d);
const camera = new THREE.PerspectiveCamera(36, innerWidth / innerHeight, 0.05, 60);
const HOME_POS = new THREE.Vector3(1.05, 0.62, 4.3);
const HOME_TARGET = new THREE.Vector3(0, 0, 0.02);
camera.position.set(0, 0.4, 8.2);
const controls = new OrbitControls(camera, renderer.domElement);
controls.target.copy(HOME_TARGET);
controls.enableDamping = true;
controls.dampingFactor = 0.06;
controls.enablePan = false;
controls.minDistance = 2.4;
controls.maxDistance = 8.5;
controls.maxPolarAngle = Math.PI * 0.68;
controls.rotateSpeed = 0.7;
controls.zoomSpeed = 0.9;
controls.autoRotate = true;
controls.autoRotateSpeed = 0.5;
controls.enabled = false;
let lastInteract = performance.now();
controls.addEventListener('start', () => { lastInteract = performance.now(); controls.autoRotate = false; });
const pmrem = new THREE.PMREMGenerator(renderer);
scene.environment = pmrem.fromScene(new RoomEnvironment(), 0.1).texture;
const key = new THREE.SpotLight(0xffffff, 150, 0, 0.48, 0.55, 1.4);
key.position.set(3.4, 3.8, 4.6);
key.castShadow = true;
key.shadow.mapSize.set(2048, 2048);
key.shadow.bias = -0.0004;
key.shadow.normalBias = 0.03;
key.shadow.camera.near = 1;
key.shadow.camera.far = 25;
scene.add(key);
scene.add(key.target);
const fill = new THREE.DirectionalLight(0xcfe0ff, 1.5);
fill.position.set(-2.6, 1.4, 2.6);
scene.add(fill);
const rim = new THREE.DirectionalLight(0xffffff, 1.3);
rim.position.set(-1.2, -2.0, -3.4);
scene.add(rim);
const accent = new THREE.SpotLight(0xffd9a0, 60, 0, 0.75, 0.8, 1.2);
accent.position.set(-3.2, 2.2, -2.4);
scene.add(accent);
scene.add(accent.target);
const hemi = new THREE.HemisphereLight(0x93a8cc, 0x0b0d12, 0.6);
scene.add(hemi);
const reflector = new Reflector(new THREE.PlaneGeometry(16, 16), {
clipBias: 0.004, textureWidth: 1024, textureHeight: 1024, color: 0x4a5566
});
reflector.rotation.x = -Math.PI / 2;
reflector.position.y = -1.75;
scene.add(reflector);
const fadeCanvas = document.createElement('canvas');
fadeCanvas.width = fadeCanvas.height = 512;
const fctx = fadeCanvas.getContext('2d');
const fg = fctx.createRadialGradient(256, 256, 50, 256, 256, 256);
fg.addColorStop(0, 'rgba(7,9,13,0)');
fg.addColorStop(0.5, 'rgba(7,9,13,0.6)');
fg.addColorStop(1, 'rgba(7,9,13,1)');
fctx.fillStyle = fg;
fctx.fillRect(0, 0, 512, 512);
const fadeTex = new THREE.CanvasTexture(fadeCanvas);
const floorFade = new THREE.Mesh(
new THREE.PlaneGeometry(16, 16),
new THREE.MeshBasicMaterial({ map: fadeTex, transparent: true, depthWrite: false })
);
floorFade.rotation.x = -Math.PI / 2;
floorFade.position.y = -1.749;
floorFade.renderOrder = 3;
scene.add(floorFade);
const glowCanvas = document.createElement('canvas');
glowCanvas.width = glowCanvas.height = 512;
const gctx = glowCanvas.getContext('2d');
const grd = gctx.createRadialGradient(256, 256, 20, 256, 256, 256);
grd.addColorStop(0, 'rgba(84,106,150,0.5)');
grd.addColorStop(0.4, 'rgba(42,54,82,0.2)');
grd.addColorStop(1, 'rgba(6,8,13,0)');
gctx.fillStyle = grd;
gctx.fillRect(0, 0, 512, 512);
const glowTex = new THREE.CanvasTexture(glowCanvas);
const glowSprite = new THREE.Sprite(new THREE.SpriteMaterial({
map: glowTex, transparent: true, depthWrite: false, blending: THREE.AdditiveBlending, opacity: 0.9
}));
glowSprite.scale.set(14, 14, 1);
glowSprite.position.set(0, 0.3, -6.5);
glowSprite.renderOrder = 1;
scene.add(glowSprite);
function cylZ(rt, rb, h, seg, openEnded) {
const g = new THREE.CylinderGeometry(rt, rb, h, seg, 1, openEnded);
g.rotateX(Math.PI / 2);
return g;
}
function latheZ(pts, seg) {
const v2 = pts.map(p => new THREE.Vector2(p[0], p[1]));
const g = new THREE.LatheGeometry(v2, seg || 96);
g.rotateX(Math.PI / 2);
return g;
}
function circlePath(r, x, y) {
const p = new THREE.Path();
p.absarc(x || 0, y || 0, r, 0, TAU, true);
return p;
}
function discShape(r) {
const s = new THREE.Shape();
s.absarc(0, 0, r, 0, TAU, false);
return s;
}
function ringShape(rIn, rOut) {
const s = discShape(rOut);
s.holes.push(circlePath(rIn));
return s;
}
function annularSectorShape(r0, r1, a0deg, a1deg) {
const s = new THREE.Shape();
const seg = Math.max(4, Math.ceil((a1deg - a0deg) / 12));
for (let i = 0; i <= seg; i++) {
const a = (a0deg + (a1deg - a0deg) * i / seg) * Math.PI / 180;
const x = Math.cos(a) * r1, y = Math.sin(a) * r1;
if (i === 0) s.moveTo(x, y); else s.lineTo(x, y);
}
for (let i = seg; i >= 0; i--) {
const a = (a0deg + (a1deg - a0deg) * i / seg) * Math.PI / 180;
s.lineTo(Math.cos(a) * r0, Math.sin(a) * r0);
}
s.closePath();
return s;
}
function gearShape(r, teeth, toothDepth) {
const s = new THREE.Shape();
const n = teeth * 2;
const root = r - toothDepth;
for (let i = 0; i <= n; i++) {
const rad = i % 2 === 0 ? r : root;
const a = (i / n) * TAU;
const x = Math.cos(a) * rad, y = Math.sin(a) * rad;
if (i === 0) s.moveTo(x, y); else s.lineTo(x, y);
}
s.closePath();
return s;
}
function gearGeo(o) {
const shape = gearShape(o.r, o.teeth, o.toothDepth);
if (o.spokes) {
for (let s = 0; s < o.spokes; s++) {
const a = (s / o.spokes) * TAU + (o.spokePhase || 0);
shape.holes.push(circlePath(o.spokeR, Math.cos(a) * o.spokePos, Math.sin(a) * o.spokePos));
}
}
const bevel = Math.min(0.006, o.toothDepth * 0.4, o.depth * 0.4);
const g = new THREE.ExtrudeGeometry(shape, {
depth: o.depth, bevelEnabled: true, bevelThickness: bevel, bevelSize: bevel, bevelSegments: 2, curveSegments: 24
});
g.center();
g.translate(0, 0, -o.depth / 2);
return g;
}
function handShape(L, w) {
const s = new THREE.Shape();
s.moveTo(L, 0);
s.lineTo(L * 0.30, w);
s.lineTo(0, w * 0.85);
s.lineTo(-L * 0.10, w * 0.5);
s.lineTo(-L * 0.10, -w * 0.5);
s.lineTo(0, -w * 0.85);
s.lineTo(L * 0.30, -w);
s.closePath();
return s;
}
class SpiralCurve extends THREE.Curve {
constructor(r0, r1, turns) { super(); this.r0 = r0; this.r1 = r1; this.turns = turns; }
getPoint(t, target) {
const a = this.turns * TAU * t;
const r = this.r0 + (this.r1 - this.r0) * t;
return (target || new THREE.Vector3()).set(Math.cos(a) * r, Math.sin(a) * r, 0);
}
}
function makePerlageTexture() {
const c = document.createElement('canvas');
c.width = c.height = 256;
const ctx = c.getContext('2d');
ctx.fillStyle = '#b6bac2';
ctx.fillRect(0, 0, 256, 256);
const cell = 16;
for (let y = cell / 2; y < 256; y += cell) {
for (let x = cell / 2; x < 256; x += cell) {
const ox = x + (Math.floor(y / cell) % 2 ? cell / 2 : 0);
const g = ctx.createRadialGradient(ox, y, 1, ox, y, 6.5);
g.addColorStop(0, '#d3d6dc');
g.addColorStop(0.7, '#a9adb6');
g.addColorStop(1, '#8a8f99');
ctx.fillStyle = g;
ctx.beginPath();
ctx.arc(ox, y, 6.5, 0, TAU);
ctx.fill();
}
}
const tex = new THREE.CanvasTexture(c);
tex.wrapS = tex.wrapT = THREE.RepeatWrapping;
tex.colorSpace = THREE.SRGBColorSpace;
return tex;
}
function makeGenevaTexture() {
const c = document.createElement('canvas');
c.width = c.height = 256;
const ctx = c.getContext('2d');
ctx.fillStyle = '#b6bac2';
ctx.fillRect(0, 0, 256, 256);
const bar = 32;
for (let x = 0; x < 256; x += bar) {
const g = ctx.createLinearGradient(x, 0, x + bar, 0);
g.addColorStop(0, '#8a8f99');
g.addColorStop(0.5, '#c8ccd4');
g.addColorStop(1, '#8a8f99');
ctx.fillStyle = g;
ctx.fillRect(x, 0, bar, 256);
}
const tex = new THREE.CanvasTexture(c);
tex.wrapS = tex.wrapT = THREE.RepeatWrapping;
tex.colorSpace = THREE.SRGBColorSpace;
return tex;
}
const perlageTex = makePerlageTexture();
const genevaTex = makeGenevaTexture();
const MAT = {
steel: () => new THREE.MeshPhysicalMaterial({ color: 0xe6e8ee, metalness: 1, roughness: 0.34, envMapIntensity: 0.6 }),
steelDark: () => new THREE.MeshPhysicalMaterial({ color: 0xb0b6c0, metalness: 1, roughness: 0.34, envMapIntensity: 0.55 }),
steelBrush: () => new THREE.MeshPhysicalMaterial({ color: 0xdfe2e9, metalness: 1, roughness: 0.5, envMapIntensity: 0.5 }),
gold: () => new THREE.MeshPhysicalMaterial({ color: 0xd8b34f, metalness: 1, roughness: 0.34, envMapIntensity: 0.75 }),
brass: () => new THREE.MeshPhysicalMaterial({ color: 0xcfae62, metalness: 1, roughness: 0.45, envMapIntensity: 0.6 }),
blue: () => new THREE.MeshPhysicalMaterial({ color: 0x2e56c8, metalness: 1, roughness: 0.36, envMapIntensity: 0.65 }),
ruby: () => new THREE.MeshPhysicalMaterial({ color: 0xa31024, metalness: 0.05, roughness: 0.08, clearcoat: 1, clearcoatRoughness: 0.05, emissive: 0x30030a, emissiveIntensity: 0.9, envMapIntensity: 0.9 }),
rubyBore: () => new THREE.MeshPhysicalMaterial({ color: 0x4a0710, metalness: 0.1, roughness: 0.2 }),
lume: () => new THREE.MeshPhysicalMaterial({ color: 0xeafff0, metalness: 0, roughness: 0.35, emissive: 0xc8ffd8, emissiveIntensity: 2.6 }),
lumeDim: () => new THREE.MeshPhysicalMaterial({ color: 0xd8e8dc, metalness: 0, roughness: 0.4, emissive: 0xa8e8b8, emissiveIntensity: 1.5 }),
dialBlack: () => new THREE.MeshPhysicalMaterial({ color: 0x17181c, metalness: 0.92, roughness: 0.32, envMapIntensity: 0.5 }),
glass: () => new THREE.MeshPhysicalMaterial({ color: 0xffffff, metalness: 0, roughness: 0.06, transmission: 1, thickness: 0.28, ior: 1.77, envMapIntensity: 1.3, specularIntensity: 0.9, attenuationColor: 0xffffff, attenuationDistance: 4, transparent: true }),
glassSmoke: () => new THREE.MeshPhysicalMaterial({ color: 0x0c0e12, metalness: 0, roughness: 0.06, transmission: 0.85, thickness: 0.2, ior: 1.5, envMapIntensity: 1.0, attenuationColor: 0x1a1e24, attenuationDistance: 0.6, transparent: true }),
perlage: () => new THREE.MeshPhysicalMaterial({ map: perlageTex, color: 0xdfe3ea, metalness: 0.92, roughness: 0.38, envMapIntensity: 0.65 }),
geneva: () => new THREE.MeshPhysicalMaterial({ map: genevaTex, color: 0xdfe3ea, metalness: 0.92, roughness: 0.32, envMapIntensity: 0.7 })
};
const jewelMeshes = [];
const screwMeshes = [];
function makeScrew(mat) {
const g = new THREE.Group();
const head = new THREE.Mesh(cylZ(0.034, 0.029, 0.026, 18), mat);
const slot = new THREE.Mesh(new THREE.BoxGeometry(0.056, 0.012, 0.010), mat);
slot.position.z = 0.014;
g.add(head, slot);
g.userData.partId = 'screw';
g.traverse(o => { o.userData.partId = 'screw'; });
screwMeshes.push(g);
return g;
}
function makeJewel() {
const g = new THREE.Group();
const chaton = new THREE.Mesh(new THREE.TorusGeometry(0.05, 0.009, 10, 28), MAT.gold());
const stone = new THREE.Mesh(cylZ(0.04, 0.04, 0.042, 20), MAT.ruby());
const bore = new THREE.Mesh(cylZ(0.014, 0.014, 0.046, 10), MAT.rubyBore());
bore.position.z = 0.002;
g.add(chaton, stone, bore);
g.userData.partId = 'jewel';
g.traverse(o => { o.userData.partId = 'jewel'; });
jewelMeshes.push(g);
return g;
}
const watch = new THREE.Group();
scene.add(watch);
const caseGroup = new THREE.Group();
{
const middle = new THREE.Mesh(latheZ([
[1.06, -0.17], [1.05, -0.08], [1.05, 0.10], [1.044, 0.14]
]), MAT.steelBrush());
caseGroup.add(middle);
[[-0.37, 1.24, 0.12], [0.37, 1.24, 0.12], [-0.37, -1.24, -0.12], [0.37, -1.24, -0.12]].forEach(p => {
const lug = new THREE.Mesh(new RoundedBoxGeometry(0.26, 0.34, 0.18, 3, 0.07), MAT.steel());
lug.position.set(p[0], p[1], -0.02);
lug.rotation.x = p[2];
caseGroup.add(lug);
});
}
watch.add(caseGroup);
const caseback = new THREE.Group();
{
const ring = new THREE.Mesh(latheZ([
[0.63, -0.172], [0.66, -0.16], [1.045, -0.16], [1.055, -0.168], [1.05, -0.176], [0.63, -0.176]
]), MAT.steel());
caseback.add(ring);
const win = new THREE.Mesh(cylZ(0.62, 0.62, 0.03, 48), MAT.glassSmoke());
win.position.z = -0.163;
caseback.add(win);
[0.78, 0.93].forEach(r => {
const t = new THREE.Mesh(new THREE.TorusGeometry(r, 0.008, 10, 96), MAT.steelDark());
t.position.z = -0.153;
caseback.add(t);
});
for (let k = 0; k < 6; k++) {
const a = (30 + 60 * k) * Math.PI / 180;
const sc = makeScrew(MAT.blue());
sc.position.set(Math.cos(a) * 0.90, Math.sin(a) * 0.90, -0.148);
sc.rotation.z = Math.random() * TAU;
caseback.add(sc);
}
}
watch.add(caseback);
const bezel = new THREE.Group();
{
const mesh = new THREE.Mesh(latheZ([
[0.90, 0.15], [0.895, 0.168], [0.905, 0.19], [1.02, 0.19], [1.05, 0.185], [1.058, 0.13], [1.04, 0.135], [1.015, 0.132]
]), MAT.steel());
bezel.add(mesh);
}
watch.add(bezel);
const crystal = new THREE.Group();
{
const profile = [];
for (let r = 0; r < 0.90; r += 0.045) {
profile.push([r, -1.128 + Math.sqrt(2.56 - r * r)]);
}
profile.push([0.90, 0.195]);
const mesh = new THREE.Mesh(latheZ(profile, 96), MAT.glass());
crystal.add(mesh);
}
watch.add(crystal);
const crownGroup = new THREE.Group();
{
const body = new THREE.Mesh(new THREE.CylinderGeometry(0.17, 0.17, 0.32, 32), MAT.steelBrush());
body.rotation.z = Math.PI / 2;
body.position.x = 0.06;
crownGroup.add(body);
for (let i = 0; i < 30; i++) {
const a = (i / 30) * TAU;
const ridge = new THREE.Mesh(new THREE.BoxGeometry(0.34, 0.032, 0.045), MAT.steelBrush());
ridge.rotation.x = a;
ridge.position.set(0.06, Math.cos(a) * 0.185, Math.sin(a) * 0.185);
crownGroup.add(ridge);
}
const cap = new THREE.Mesh(new THREE.CylinderGeometry(0.185, 0.185, 0.035, 32), MAT.steel());
cap.rotation.z = Math.PI / 2;
cap.position.x = 0.235;
crownGroup.add(cap);
const dot = new THREE.Mesh(new THREE.CylinderGeometry(0.055, 0.055, 0.015, 24), MAT.gold());
dot.rotation.z = Math.PI / 2;
dot.position.x = 0.255;
crownGroup.add(dot);
const tube = new THREE.Mesh(new THREE.CylinderGeometry(0.075, 0.075, 0.24, 24), MAT.steelBrush());
tube.rotation.z = Math.PI / 2;
tube.position.x = -0.15;
crownGroup.add(tube);
crownGroup.position.set(1.02, 0, 0.03);
}
watch.add(crownGroup);
const mainPlate = new THREE.Group();
{
const shape = discShape(0.90);
shape.holes.push(circlePath(0.13));
[[105, 0.58], [160, 0.58], [235, 0.58], [300, 0.58]].forEach(h => {
const a = h[0] * Math.PI / 180;
shape.holes.push(circlePath(0.075, Math.cos(a) * h[1], Math.sin(a) * h[1]));
});
const mesh = new THREE.Mesh(new THREE.ExtrudeGeometry(shape, {
depth: 0.10, bevelEnabled: true, bevelThickness: 0.012, bevelSize: 0.012, bevelSegments: 2, curveSegments: 64
}), MAT.perlage());
mesh.position.z = -0.05;
mainPlate.add(mesh);
for (let k = 0; k < 8; k++) {
const a = (22.5 + 45 * k) * Math.PI / 180;
const sc = makeScrew(MAT.blue());
sc.position.set(Math.cos(a) * 0.84, Math.sin(a) * 0.84, 0.055);
sc.rotation.z = Math.random() * TAU;
mainPlate.add(sc);
}
[[-0.05, 0.55], [-0.42, 0.05], [0.25, -0.10]].forEach(p => {
const j = makeJewel();
j.position.set(p[0], p[1], 0.065);
mainPlate.add(j);
});
}
watch.add(mainPlate);
const barrel = new THREE.Group();
{
const gear = new THREE.Mesh(gearGeo({ r: 0.30, teeth: 84, toothDepth: 0.024, depth: 0.022, spokes: 5, spokeR: 0.048, spokePos: 0.18 }), MAT.brass());
gear.position.z = 0.028;
barrel.add(gear);
const drum = new THREE.Mesh(cylZ(0.235, 0.235, 0.095, 48), MAT.steelBrush());
drum.position.z = 0.065;
barrel.add(drum);
[0.14, 0.19].forEach(r => {
const t = new THREE.Mesh(new THREE.TorusGeometry(r, 0.005, 8, 48), MAT.gold());
t.position.z = 0.115;
barrel.add(t);
});
const arbour = new THREE.Mesh(cylZ(0.035, 0.035, 0.20, 20), MAT.gold());
arbour.position.z = 0.08;
barrel.add(arbour);
const ratchet = new THREE.Mesh(gearGeo({ r: 0.15, teeth: 40, toothDepth: 0.016, depth: 0.024 }), MAT.steel());
ratchet.position.z = 0.175;
barrel.add(ratchet);
const ratchetScrew = makeScrew(MAT.blue());
ratchetScrew.position.z = 0.19;
barrel.add(ratchetScrew);
barrel.position.set(0.44, 0.30, 0);
}
watch.add(barrel);
const centerWheel = new THREE.Group();
{
const gear = new THREE.Mesh(gearGeo({ r: 0.16, teeth: 64, toothDepth: 0.015, depth: 0.02, spokes: 5, spokeR: 0.026, spokePos: 0.096 }), MAT.brass());
gear.position.z = 0.035;
centerWheel.add(gear);
const hub = new THREE.Mesh(cylZ(0.055, 0.055, 0.045, 24), MAT.gold());
hub.position.z = 0.05;
centerWheel.add(hub);
}
watch.add(centerWheel);
const thirdWheel = new THREE.Group();
{
const gear = new THREE.Mesh(gearGeo({ r: 0.13, teeth: 56, toothDepth: 0.013, depth: 0.02, spokes: 4, spokeR: 0.021, spokePos: 0.078 }), MAT.brass());
gear.position.z = 0.04;
thirdWheel.add(gear);
const hub = new THREE.Mesh(cylZ(0.04, 0.04, 0.04, 20), MAT.gold());
hub.position.z = 0.052;
thirdWheel.add(hub);
thirdWheel.position.set(-0.26, 0.24, 0);
}
watch.add(thirdWheel);
const fourthWheel = new THREE.Group();
{
const gear = new THREE.Mesh(gearGeo({ r: 0.075, teeth: 40, toothDepth: 0.009, depth: 0.016 }), MAT.gold());
gear.position.z = 0.06;
fourthWheel.add(gear);
const hub = new THREE.Mesh(cylZ(0.03, 0.03, 0.05, 16), MAT.gold());
hub.position.z = 0.075;
fourthWheel.add(hub);
const post = new THREE.Mesh(cylZ(0.014, 0.014, 0.24, 12), MAT.gold());
post.position.z = 0.13;
fourthWheel.add(post);
}
watch.add(fourthWheel);
const escapeWheel = new THREE.Group();
{
const gear = new THREE.Mesh(gearGeo({ r: 0.13, teeth: 15, toothDepth: 0.016, depth: 0.018, spokes: 4, spokeR: 0.021, spokePos: 0.078 }), MAT.steel());
gear.position.z = 0.045;
escapeWheel.add(gear);
const hub = new THREE.Mesh(cylZ(0.035, 0.035, 0.04, 16), MAT.steel());
hub.position.z = 0.055;
escapeWheel.add(hub);
escapeWheel.position.set(-0.40, -0.30, 0);
}
watch.add(escapeWheel);
const palletFork = new THREE.Group();
{
const arm = new THREE.Mesh(new THREE.BoxGeometry(0.03, 0.15, 0.018), MAT.steel());
arm.position.y = 0.07;
palletFork.add(arm);
const hornL = new THREE.Mesh(new THREE.BoxGeometry(0.02, 0.055, 0.016), MAT.steel());
hornL.position.set(-0.028, 0.138, 0);
palletFork.add(hornL);
const hornR = hornL.clone();
hornR.position.x = 0.028;
palletFork.add(hornR);
const palletArm = new THREE.Mesh(new THREE.BoxGeometry(0.028, 0.13, 0.016), MAT.steel());
palletArm.position.y = -0.06;
palletFork.add(palletArm);
const stoneL = new THREE.Mesh(new THREE.BoxGeometry(0.014, 0.022, 0.013), MAT.ruby());
stoneL.position.set(-0.03, -0.12, 0);
palletFork.add(stoneL);
const stoneR = stoneL.clone();
stoneR.position.x = 0.03;
palletFork.add(stoneR);
const hub = new THREE.Mesh(cylZ(0.045, 0.045, 0.05, 20), MAT.gold());
palletFork.add(hub);
palletFork.position.set(-0.168, -0.416, 0.075);
palletFork.rotation.z = Math.atan2(-0.104, 0.208) - Math.PI / 2;
}
watch.add(palletFork);
const balance = new THREE.Group();
{
const rim = new THREE.Mesh(new THREE.TorusGeometry(0.31, 0.026, 16, 128), MAT.gold());
rim.position.z = 0.06;
balance.add(rim);
for (let k = 0; k < 4; k++) {
const a = k * Math.PI / 2;
const arm = new THREE.Mesh(new THREE.BoxGeometry(0.055, 0.26, 0.02), MAT.gold());
arm.position.set(Math.cos(a) * 0.145, Math.sin(a) * 0.145, 0.06);
arm.rotation.z = a;
balance.add(arm);
}
for (let k = 0; k < 12; k++) {
const a = (15 + 30 * k) * Math.PI / 180;
const sc = new THREE.Mesh(cylZ(0.016, 0.016, 0.024, 12), MAT.gold());
sc.position.set(Math.cos(a) * 0.31, Math.sin(a) * 0.31, 0.06);
balance.add(sc);
}
const hub = new THREE.Mesh(cylZ(0.05, 0.05, 0.07, 20), MAT.gold());
hub.position.z = 0.06;
balance.add(hub);
const staff = new THREE.Mesh(cylZ(0.012, 0.012, 0.24, 10), MAT.steel());
staff.position.z = 0.10;
balance.add(staff);
const roller = new THREE.Mesh(cylZ(0.10, 0.10, 0.022, 32), MAT.gold());
roller.position.z = 0.032;
balance.add(roller);
const pin = new THREE.Mesh(cylZ(0.017, 0.017, 0.04, 10), MAT.ruby());
pin.position.set(0, -0.10, 0.032);
balance.add(pin);
balance.position.set(0.04, -0.52, 0);
}
watch.add(balance);
const hairspring = new THREE.Group();
{
const spring = new THREE.Mesh(
new THREE.TubeGeometry(new SpiralCurve(0.02, 0.19, 10.5), 420, 0.0075, 6, false),
MAT.blue()
);
hairspring.add(spring);
const collet = new THREE.Mesh(new THREE.TorusGeometry(0.028, 0.008, 8, 24), MAT.gold());
collet.position.z = -0.005;
hairspring.add(collet);
const stud = new THREE.Mesh(cylZ(0.012, 0.012, 0.03, 10), MAT.steel());
stud.position.set(0.19, 0, 0);
hairspring.add(stud);
hairspring.position.z = 0.12;
}
balance.add(hairspring);
const trainBridge = new THREE.Group();
{
const box = new THREE.Mesh(new RoundedBoxGeometry(0.66, 0.26, 0.05, 4, 0.07), MAT.geneva());
trainBridge.add(box);
const j1 = makeJewel();
j1.position.set(0.13, 0, 0.04);
trainBridge.add(j1);
const j2 = makeJewel();
j2.position.set(-0.13, 0, 0.04);
trainBridge.add(j2);
const s1 = makeScrew(MAT.blue());
s1.position.set(0.28, 0, 0.03);
s1.rotation.z = Math.random() * TAU;
trainBridge.add(s1);
const s2 = makeScrew(MAT.blue());
s2.position.set(-0.28, 0, 0.03);
s2.rotation.z = Math.random() * TAU;
trainBridge.add(s2);
trainBridge.position.set(-0.13, 0.12, 0.135);
trainBridge.rotation.z = Math.atan2(0.24, -0.26);
}
watch.add(trainBridge);
const barrelBridge = new THREE.Group();
{
const ring = new THREE.Mesh(new THREE.ExtrudeGeometry(ringShape(0.16, 0.34), {
depth: 0.05, bevelEnabled: true, bevelThickness: 0.01, bevelSize: 0.01, bevelSegments: 2, curveSegments: 48
}), MAT.geneva());
barrelBridge.add(ring);
const s1 = makeScrew(MAT.blue());
s1.position.set(0.226, 0.105, 0.06);
s1.rotation.z = Math.random() * TAU;
barrelBridge.add(s1);
const s2 = makeScrew(MAT.blue());
s2.position.set(-0.226, -0.105, 0.06);
s2.rotation.z = Math.random() * TAU;
barrelBridge.add(s2);
barrelBridge.position.set(0.44, 0.30, 0.11);
}
watch.add(barrelBridge);
const balanceCock = new THREE.Group();
{
const sector = new THREE.Mesh(new THREE.ExtrudeGeometry(annularSectorShape(0.10, 0.29, -170, -75), {
depth: 0.05, bevelEnabled: true, bevelThickness: 0.008, bevelSize: 0.008, bevelSegments: 2, curveSegments: 32
}), MAT.geneva());
balanceCock.add(sector);
const foot = new THREE.Mesh(new THREE.ExtrudeGeometry(discShape(0.085), {
depth: 0.05, bevelEnabled: true, bevelThickness: 0.01, bevelSize: 0.01, bevelSegments: 2, curveSegments: 32
}), MAT.geneva());
foot.position.set(-0.1226, -0.2628, 0);
balanceCock.add(foot);
const footScrew = makeScrew(MAT.blue());
footScrew.position.set(-0.1226, -0.2628, 0.06);
footScrew.rotation.z = Math.random() * TAU;
balanceCock.add(footScrew);
const jewel = makeJewel();
jewel.position.z = 0.055;
balanceCock.add(jewel);
const cap = new THREE.Mesh(cylZ(0.075, 0.075, 0.014, 28), MAT.gold());
cap.position.z = 0.073;
balanceCock.add(cap);
const cs1 = makeScrew(MAT.blue());
cs1.scale.setScalar(0.55);
cs1.position.set(0.052, 0, 0.077);
balanceCock.add(cs1);
const cs2 = makeScrew(MAT.blue());
cs2.scale.setScalar(0.55);
cs2.position.set(-0.052, 0, 0.077);
balanceCock.add(cs2);
balanceCock.position.set(0.04, -0.52, 0.13);
}
watch.add(balanceCock);
const palletCock = new THREE.Group();
{
const box = new THREE.Mesh(new RoundedBoxGeometry(0.32, 0.15, 0.05, 3, 0.06), MAT.geneva());
palletCock.add(box);
const jewel = makeJewel();
jewel.position.z = 0.04;
palletCock.add(jewel);
const sc = makeScrew(MAT.blue());
sc.position.set(0.10, 0, 0.03);
sc.rotation.z = Math.random() * TAU;
palletCock.add(sc);
palletCock.position.set(-0.168, -0.416, 0.135);
palletCock.rotation.z = Math.atan2(-0.104, 0.208);
}
watch.add(palletCock);
const dial = new THREE.Group();
{
const ring = new THREE.Mesh(new THREE.ExtrudeGeometry(ringShape(0.83, 0.92), {
depth: 0.025, bevelEnabled: true, bevelThickness: 0.006, bevelSize: 0.006, bevelSegments: 2, curveSegments: 64
}), MAT.dialBlack());
ring.position.z = 0.152;
dial.add(ring);
for (let k = 0; k < 12; k++) {
const a = k * Math.PI / 6;
const L = k % 3 === 0 ? 0.22 : 0.15;
const b = new THREE.Mesh(new THREE.BoxGeometry(0.045, L, 0.028), MAT.lume());
b.position.set(Math.cos(a) * 0.875, Math.sin(a) * 0.875, 0.175);
b.rotation.z = a;
dial.add(b);
}
for (let m = 0; m < 60; m++) {
if (m % 5 === 0) continue;
const a = m * Math.PI / 30;
const d = new THREE.Mesh(cylZ(0.011, 0.011, 0.012, 12), MAT.lumeDim());
d.position.set(Math.cos(a) * 0.855, Math.sin(a) * 0.855, 0.172);
dial.add(d);
}
}
watch.add(dial);
const hourHand = new THREE.Group();
{
const mesh = new THREE.Mesh(new THREE.ExtrudeGeometry(handShape(0.44, 0.055), {
depth: 0.016, bevelEnabled: true, bevelThickness: 0.004, bevelSize: 0.004, bevelSegments: 2
}), MAT.steel());
hourHand.add(mesh);
const lume = new THREE.Mesh(new THREE.ExtrudeGeometry(handShape(0.28, 0.028), {
depth: 0.008, bevelEnabled: false
}), MAT.lume());
lume.position.set(0.13, 0, 0.012);
hourHand.add(lume);
const cap = new THREE.Mesh(cylZ(0.028, 0.028, 0.02, 20), MAT.gold());
cap.position.z = 0.014;
hourHand.add(cap);
hourHand.position.z = 0.235;
}
watch.add(hourHand);
const minuteHand = new THREE.Group();
{
const mesh = new THREE.Mesh(new THREE.ExtrudeGeometry(handShape(0.62, 0.045), {
depth: 0.016, bevelEnabled: true, bevelThickness: 0.004, bevelSize: 0.004, bevelSegments: 2
}), MAT.steel());
minuteHand.add(mesh);
const lume = new THREE.Mesh(new THREE.ExtrudeGeometry(handShape(0.40, 0.022), {
depth: 0.008, bevelEnabled: false
}), MAT.lume());
lume.position.set(0.19, 0, 0.012);
minuteHand.add(lume);
const cap = new THREE.Mesh(cylZ(0.028, 0.028, 0.02, 20), MAT.gold());
cap.position.z = 0.014;
minuteHand.add(cap);
minuteHand.position.z = 0.255;
}
watch.add(minuteHand);
const secondHand = new THREE.Group();
{
const needle = new THREE.Mesh(new THREE.ExtrudeGeometry(handShape(0.66, 0.010), {
depth: 0.008, bevelEnabled: false
}), MAT.blue());
secondHand.add(needle);
const cw = new THREE.Mesh(cylZ(0.034, 0.034, 0.012, 20), MAT.gold());
cw.position.set(-0.09, 0, 0.004);
secondHand.add(cw);
const dot = new THREE.Mesh(cylZ(0.026, 0.026, 0.012, 20), MAT.lume());
dot.position.set(0.55, 0, 0.004);
secondHand.add(dot);
const cap = new THREE.Mesh(cylZ(0.03, 0.03, 0.014, 20), MAT.gold());
cap.position.z = 0.006;
secondHand.add(cap);
secondHand.position.z = 0.275;
}
watch.add(secondHand);
const INFO = {
case: { name: '表壳', en: 'CASE', desc: '42mm 圆形表壳,由整块 904L 精钢切割而成,侧面镜面抛光与拉丝交替处理,光影流转间尽显质感。', specs: [['材质', '904L 精钢'], ['尺寸', '42 mm'], ['工艺', '抛光 / 拉丝交替打磨']] },
bezel: { name: '表圈', en: 'BEZEL', desc: '高抛光斜面表圈,以柔和弧度收拢光线,勾勒出镂空表盘的深邃轮廓。', specs: [['材质', '抛光精钢'], ['工艺', '高光倒角']] },
crown: { name: '表冠', en: 'CROWN', desc: '凹槽式表冠,防滑纹理便于上链与调校;旋入式结构确保 50 米防水。', specs: [['材质', '精钢'], ['工艺', '防滑凹槽纹'], ['功能', '手动上链 · 调时']] },
crystal: { name: '蓝宝石水晶玻璃', en: 'SAPPHIRE CRYSTAL', desc: '弧形蓝宝石水晶玻璃,双面防眩光涂层,莫氏硬度 9,仅次于钻石,为机芯提供一览无余的窗口。', specs: [['材质', '蓝宝石水晶'], ['硬度', '莫氏 9'], ['处理', '双面防眩涂层']] },
dial: { name: '镂空刻度环', en: 'CHAPTER RING', desc: '镂空刻度环与立体夜光时标,摒弃传统表盘,让整个轮系成为主角。', specs: [['材质', '精钢'], ['工艺', '镂空 + 立体夜光']] },
hourHand: { name: '时针', en: 'HOUR HAND', desc: '抛光剑形时针,内嵌 Super-LumiNova 夜光涂层,暗处亦清晰可读。', specs: [['材质', '抛光精钢'], ['夜光', 'Super-LumiNova']] },
minuteHand: { name: '分针', en: 'MINUTE HAND', desc: '与时针同款的剑形分针,末端纤细锐利,直指分钟刻度。', specs: [['材质', '抛光精钢'], ['夜光', 'Super-LumiNova']] },
secondHand: { name: '秒针', en: 'SECOND HAND', desc: '烤蓝精钢秒针,纤细如丝,尾端圆形配重与夜光圆点形成优雅平衡。', specs: [['材质', '烤蓝精钢'], ['夜光', '尖端圆点']] },
barrel: { name: '主发条盒', en: 'MAINSPRING BARREL', desc: '主发条盒——机芯的动力之源。盒内发条储存约 72 小时动力,经齿轮系逐级传递。', specs: [['功能', '72 小时动力储存'], ['材质', '精钢发条']] },
centerWheel: { name: '中心轮', en: 'CENTRE WHEEL', desc: '中心轮直接与发条盒齿轮啮合,驱动时、分指针,是轮系的第二级。', specs: [['材质', '黄铜镀金'], ['功能', '驱动时 / 分针']] },
thirdWheel: { name: '三轮', en: 'THIRD WHEEL', desc: '三轮承接中心轮动力,继续放大转速,是能量传递的中枢。', specs: [['材质', '黄铜镀金'], ['功能', '传动加速']] },
fourthWheel: { name: '秒轮', en: 'FOURTH WHEEL', desc: '秒轮与中心同轴,以每分钟一周的稳定速度驱动中央秒针。', specs: [['材质', '黄铜镀金'], ['功能', '驱动中央秒针']] },
escapeWheel: { name: '擒纵轮', en: 'ESCAPE WHEEL', desc: '15 齿擒纵轮,每次擒纵叉摆动释放一齿,把持续动力切分为清脆的滴答声。', specs: [['材质', '抛光精钢'], ['齿数', '15'], ['功能', '分解放动能量']] },
palletFork: { name: '擒纵叉', en: 'PALLET FORK', desc: '擒纵叉以红宝石叉瓦与擒纵轮啮合,将能量递送给摆轮,维持其永不疲倦的摆动。', specs: [['材质', '精钢'], ['宝石', '红宝石叉瓦'], ['功能', '擒纵锁接']] },
balance: { name: '摆轮', en: 'BALANCE WHEEL', desc: '摆轮——机芯的心脏。配重螺丝与双臂环设计,以 4 Hz 频率稳定摆动,主宰走时精度。', specs: [['材质', '镀金双金属'], ['频率', '4 Hz (28\'800 vph)'], ['功能', '调速振荡']] },
hairspring: { name: '游丝', en: 'HAIRSPRING', desc: '蓝钢宝玑式游丝,与摆轮构成振荡系统,如同微型的呼吸,决定每一次滴答的节奏。', specs: [['材质', '蓝钢游丝'], ['结构', '宝玑上绕'], ['功能', '等时振荡']] },
mainPlate: { name: '主夹板', en: 'MAIN PLATE', desc: '镀铑主夹板,遍布传统珍珠纹打磨,是支撑全部轮系与夹板的基石。', specs: [['材质', '镀铑黄铜'], ['工艺', '珍珠纹打磨']] },
barrelBridge: { name: '发条盒夹板', en: 'BARREL BRIDGE', desc: '发条盒夹板,日内瓦条纹打磨,以蓝钢螺丝固定,镶嵌红宝石轴承。', specs: [['材质', '镀铑'], ['工艺', '日内瓦条纹']] },
trainBridge: { name: '轮系夹板', en: 'TRAIN BRIDGE', desc: '轮系夹板横跨中心轮与三轮,两端红宝石轴承确保轮轴运转顺滑。', specs: [['材质', '镀铑'], ['工艺', '日内瓦条纹 + 倒角']] },
balanceCock: { name: '摆轮夹板', en: 'BALANCE COCK', desc: '摆轮夹板以优雅曲线悬架于摆轮之上,末端蓝钢螺丝固定,其上镶嵌轴承宝石。', specs: [['材质', '镀铑'], ['工艺', '雕刻曲线']] },
palletCock: { name: '擒纵叉夹板', en: 'PALLET COCK', desc: '擒纵叉夹板小巧精致,固定擒纵叉枢轴,配红宝石轴承。', specs: [['材质', '镀铑'], ['功能', '固定擒纵叉']] },
jewel: { name: '红宝石轴承', en: 'JEWEL BEARING', desc: '人造红宝石轴承,摩擦系数极低、经久耐磨,全表共 19 颗。', specs: [['材质', '人造红宝石'], ['数量', '19 石'], ['功能', '减摩轴承']] },
screw: { name: '蓝钢螺丝', en: 'BLUED SCREW', desc: '蓝钢螺丝经传统烤蓝工艺处理,淬火后呈现深邃的湛蓝光泽。', specs: [['材质', '蓝钢'], ['工艺', '传统烤蓝']] },
caseback: { name: '透视底盖', en: 'CASE BACK', desc: '蓝宝石透视底盖,将机芯背面同样精湛的打磨工艺展露无遗。', specs: [['材质', '精钢'], ['窗口', '蓝宝石玻璃'], ['功能', '透视机芯']] }
};
const PART_LIST = [
['case', '表壳', 'CASE', '#ccd4df'],
['bezel', '表圈', 'BEZEL', '#e6eaf1'],
['crown', '表冠', 'CROWN', '#b9c2cf'],
['crystal', '蓝宝石水晶玻璃', 'SAPPHIRE CRYSTAL', '#8fd0ff'],
['dial', '镂空刻度环', 'CHAPTER RING', '#4a515f'],
['hourHand', '时针', 'HOUR HAND', '#dbe2ec'],
['minuteHand', '分针', 'MINUTE HAND', '#dbe2ec'],
['secondHand', '秒针', 'SECOND HAND', '#4a6fe0'],
['barrel', '主发条盒', 'MAINSPRING BARREL', '#c9a45c'],
['centerWheel', '中心轮', 'CENTRE WHEEL', '#d8b34f'],
['thirdWheel', '三轮', 'THIRD WHEEL', '#d8b34f'],
['fourthWheel', '秒轮', 'FOURTH WHEEL', '#d8b34f'],
['escapeWheel', '擒纵轮', 'ESCAPE WHEEL', '#dfe2e9'],
['palletFork', '擒纵叉', 'PALLET FORK', '#cfd6e0'],
['balance', '摆轮', 'BALANCE WHEEL', '#e2c163'],
['hairspring', '游丝', 'HAIRSPRING', '#3f66d8'],
['mainPlate', '主夹板', 'MAIN PLATE', '#ccd4df'],
['barrelBridge', '发条盒夹板', 'BARREL BRIDGE', '#ccd4df'],
['trainBridge', '轮系夹板', 'TRAIN BRIDGE', '#ccd4df'],
['balanceCock', '摆轮夹板', 'BALANCE COCK', '#ccd4df'],
['palletCock', '擒纵叉夹板', 'PALLET COCK', '#ccd4df'],
['jewel', '红宝石轴承', 'JEWEL BEARING', '#c01830'],
['screw', '蓝钢螺丝', 'BLUED SCREW', '#2e56c8'],
['caseback', '透视底盖', 'CASE BACK', '#ccd4df']
];
const EXPLODE = {
crystal: [0, 0, 1.2], bezel: [0, 0, 0.95], crown: [0.6, 0, 0], dial: [0, 0, 0.5],
hourHand: [0, 0, 0.62], minuteHand: [0, 0, 0.82], secondHand: [0, 0, 1.02],
barrelBridge: [0, 0, 0.44], trainBridge: [0, 0, 0.40], balanceCock: [0, 0, 0.36], palletCock: [0, 0, 0.32],
barrel: [0, 0, -0.34], centerWheel: [0, 0, -0.22], thirdWheel: [0, 0, -0.26], fourthWheel: [0, 0, -0.30],
escapeWheel: [0, 0, -0.28], palletFork: [0, 0, -0.33], balance: [0, 0, -0.42],
mainPlate: [0, 0, -0.55], caseback: [0, 0, -0.85]
};
const parts = {};
function register(id, group) {
parts[id] = { group, meshes: null, home: group.position.clone(), explode: null, explodedPos: null, name: INFO[id].name, en: INFO[id].en };
if (EXPLODE[id]) {
parts[id].explode = new THREE.Vector3(EXPLODE[id][0], EXPLODE[id][1], EXPLODE[id][2]);
parts[id].explodedPos = parts[id].home.clone().add(parts[id].explode);
}
group.userData.partId = id;
group.traverse(o => { if (o.userData.partId === undefined) o.userData.partId = id; });
}
register('case', caseGroup);
register('caseback', caseback);
register('bezel', bezel);
register('crystal', crystal);
register('crown', crownGroup);
register('mainPlate', mainPlate);
register('barrel', barrel);
register('centerWheel', centerWheel);
register('thirdWheel', thirdWheel);
register('fourthWheel', fourthWheel);
register('escapeWheel', escapeWheel);
register('palletFork', palletFork);
register('hairspring', hairspring);
register('balance', balance);
register('trainBridge', trainBridge);
register('barrelBridge', barrelBridge);
register('balanceCock', balanceCock);
register('palletCock', palletCock);
register('dial', dial);
register('hourHand', hourHand);
register('minuteHand', minuteHand);
register('secondHand', secondHand);
parts.jewel = { group: null, meshes: jewelMeshes, home: null, explode: null, explodedPos: null, name: INFO.jewel.name, en: INFO.jewel.en };
parts.screw = { group: null, meshes: screwMeshes, home: null, explode: null, explodedPos: null, name: INFO.screw.name, en: INFO.screw.en };
crystal.userData.noRay = true;
crystal.traverse(o => { o.userData.noRay = true; });
watch.traverse(o => { if (o.isMesh) { o.castShadow = true; o.receiveShadow = true; } });
crystal.traverse(o => { if (o.isMesh) { o.castShadow = false; o.receiveShadow = false; } });
const composer = new EffectComposer(renderer);
composer.addPass(new RenderPass(scene, camera));
const bloomPass = new UnrealBloomPass(new THREE.Vector2(innerWidth, innerHeight), 0.32, 0.45, 1.3);
composer.addPass(bloomPass);
composer.addPass(new OutputPass());
composer.setPixelRatio(Math.min(devicePixelRatio, 2));
const legendEl = document.getElementById('legend');
PART_LIST.forEach(p => {
const chip = document.createElement('div');
chip.className = 'chip';
chip.innerHTML = `<span class="dot" style="background:${p[3]}"></span>${p[1]}`;
chip.addEventListener('mouseenter', () => { hoverPart = p[0]; });
chip.addEventListener('mouseleave', () => { if (hoverPart === p[0]) hoverPart = null; });
chip.addEventListener('click', () => { pinPart(p[0], null); });
legendEl.appendChild(chip);
});
const tooltip = document.getElementById('tooltip');
const infoPanel = document.getElementById('info');
const infoName = document.getElementById('infoName');
const infoEn = document.getElementById('infoEn');
const infoDesc = document.getElementById('infoDesc');
const infoSpecs = document.getElementById('infoSpecs');
document.getElementById('infoClose').addEventListener('click', clearPin);
function showInfo(id) {
const d = INFO[id];
if (!d) return;
infoName.textContent = d.name;
infoEn.textContent = d.en;
infoDesc.textContent = d.desc;
infoSpecs.innerHTML = d.specs.map(s => `<tr><td>${s[0]}</td><td>${s[1]}</td></tr>`).join('');
infoPanel.classList.add('open');
}
function closeInfo() { infoPanel.classList.remove('open'); }
const ray = new THREE.Raycaster();
const ndc = new THREE.Vector2();
let hoverPart = null;
let hlCurrent = null;
let pinnedPart = null;
let pinnedObj = null;
let pinEl = null;
function forEachMeshOf(id, fn) {
const p = parts[id];
if (!p) return;
if (p.group) p.group.traverse(o => { if (o.isMesh) fn(o); });
else if (p.meshes) p.meshes.forEach(m => m.traverse(o => { if (o.isMesh) fn(o); }));
}
const hlStore = new WeakMap();
function setPartHighlight(id, on) {
if (!parts[id]) return;
forEachMeshOf(id, o => {
const m = o.material;
if (!m || m.emissive === undefined) return;
if (on) {
if (!hlStore.has(o)) hlStore.set(o, { e: m.emissive.getHex(), i: m.emissiveIntensity });
m.emissive.set(0x4d7dff);
m.emissiveIntensity = 0.45;
} else if (hlStore.has(o)) {
const s = hlStore.get(o);
m.emissive.setHex(s.e);
m.emissiveIntensity = s.i;
hlStore.delete(o);
}
});
}
function findPartId(obj) {
let o = obj;
while (o) {
if (o.userData && o.userData.noRay) return null;
if (o.userData && o.userData.partId) return o.userData.partId;
o = o.parent;
}
return null;
}
function pick(e) {
ndc.set((e.clientX / innerWidth) * 2 - 1, -(e.clientY / innerHeight) * 2 + 1);
ray.setFromCamera(ndc, camera);
const hits = ray.intersectObjects(watch.children, true);
for (const h of hits) {
const id = findPartId(h.object);
if (id) return { id, obj: h.object };
}
return null;
}
function pinPart(id, obj) {
clearPin(false);
pinnedPart = id;
pinnedObj = obj || (parts[id].meshes ? parts[id].meshes[0] : parts[id].group);
pinEl = document.createElement('div');
pinEl.className = 'pin-label';
pinEl.textContent = INFO[id].name;
document.body.appendChild(pinEl);
showInfo(id);
}
function clearPin(close) {
pinnedPart = null;
pinnedObj = null;
if (pinEl) { pinEl.remove(); pinEl = null; }
if (close !== false) closeInfo();
}
window.addEventListener('keydown', e => { if (e.key === 'Escape') clearPin(); });
let downInfo = null;
renderer.domElement.addEventListener('pointerdown', e => {
downInfo = { x: e.clientX, y: e.clientY, t: performance.now() };
});
renderer.domElement.addEventListener('pointermove', e => {
if (downInfo) return;
const res = pick(e);
hoverPart = res ? res.id : null;
if (res) {
tooltip.textContent = `${INFO[res.id].name} · ${INFO[res.id].en}`;
tooltip.style.left = Math.min(e.clientX + 16, innerWidth - 220) + 'px';
tooltip.style.top = (e.clientY + 18) + 'px';
tooltip.style.opacity = '1';
} else {
tooltip.style.opacity = '0';
}
});
renderer.domElement.addEventListener('pointerup', e => {
if (!downInfo) return;
const moved = Math.hypot(e.clientX - downInfo.x, e.clientY - downInfo.y) > 5 || performance.now() - downInfo.t > 600;
downInfo = null;
if (moved) return;
const res = pick(e);
if (res) pinPart(res.id, res.obj);
else clearPin();
});
let exploded = false;
let paused = false;
document.getElementById('btnExplode').addEventListener('click', function () {
exploded = !exploded;
this.classList.toggle('active', exploded);
this.textContent = exploded ? '复原视图' : '爆炸视图';
});
document.getElementById('btnPause').addEventListener('click', function () {
paused = !paused;
this.classList.toggle('active', paused);
this.textContent = paused ? '继续' : '暂停';
if (paused) controls.autoRotate = false;
});
document.getElementById('btnReset').addEventListener('click', () => {
flyTo(HOME_POS, HOME_TARGET, 1.0);
});
let tween = null;
function flyTo(pos, targetPos, dur) {
tween = { t: 0, dur, fromPos: camera.position.clone(), toPos: pos.clone(), fromT: controls.target.clone(), toT: targetPos.clone() };
controls.enabled = false;
controls.autoRotate = false;
}
const clock = new THREE.Clock();
let simTime = 0;
const s0 = (31 / 60) * TAU;
const m0 = ((10 + 31 / 60) / 60) * TAU;
const h0 = ((10 + 10 / 60) / 12) * TAU;
let sAngle = s0, mAngle = m0, hAngle = h0;
const tmpV = new THREE.Vector3();
function animate() {
requestAnimationFrame(animate);
const dt = Math.min(clock.getDelta(), 0.1);
if (!paused) simTime += dt;
const st = simTime;
if (tween) {
tween.t += dt;
const e = Math.min(tween.t / tween.dur, 1);
const k = 1 - Math.pow(1 - e, 3);
camera.position.lerpVectors(tween.fromPos, tween.toPos, k);
controls.target.lerpVectors(tween.fromT, tween.toT, k);
if (e >= 1) { tween = null; controls.enabled = true; lastInteract = performance.now(); }
}
if (!controls.autoRotate && !paused && controls.enabled && !tween && performance.now() - lastInteract > 3500) {
controls.autoRotate = true;
}
controls.update();
const k = 1 - Math.exp(-dt * 4.5);
for (const id in parts) {
const p = parts[id];
if (!p.explode) continue;
p.group.position.lerp(exploded ? p.explodedPos : p.home, k);
}
sAngle = s0 + st * TAU / 60;
mAngle = m0 + st * TAU / 3600;
hAngle = h0 + st * TAU / 43200;
hourHand.rotation.z = hAngle;
minuteHand.rotation.z = mAngle;
secondHand.rotation.z = sAngle;
fourthWheel.rotation.z = sAngle;
barrel.rotation.z = st * 0.1;
centerWheel.rotation.z = -st * 0.8;
thirdWheel.rotation.z = st * 3.2;
escapeWheel.rotation.z = Math.floor(st * 3.2) * (TAU / 15);
palletFork.rotation.z = 0.15 * Math.tanh(4 * Math.sin(TAU * 1.6 * st - 0.35));
balance.rotation.z = 4.1 * Math.sin(TAU * 1.6 * st) * (1 + 0.05 * Math.sin(st * 0.8));
hairspring.scale.setScalar(1 + 0.05 * Math.sin(TAU * 1.6 * st));
const want = pinnedPart || hoverPart;
if (want !== hlCurrent) {
if (hlCurrent) setPartHighlight(hlCurrent, false);
if (want) setPartHighlight(want, true);
hlCurrent = want;
}
if (pinnedPart) {
const pulse = 0.4 + 0.2 * Math.sin(performance.now() * 0.005);
forEachMeshOf(pinnedPart, o => { if (o.material && o.material.emissiveIntensity !== undefined) o.material.emissiveIntensity = pulse; });
}
renderer.domElement.style.cursor = want ? 'pointer' : '';
if (pinnedObj) {
pinnedObj.getWorldPosition(tmpV);
tmpV.project(camera);
if (tmpV.z < 1) {
pinEl.style.opacity = '1';
pinEl.style.left = (tmpV.x * 0.5 + 0.5) * innerWidth + 'px';
pinEl.style.top = (1 - (tmpV.y * 0.5 + 0.5)) * innerHeight - 30 + 'px';
} else {
pinEl.style.opacity = '0';
}
}
composer.render();
}
flyTo(HOME_POS, HOME_TARGET, 2.6);
animate();
setTimeout(() => {
const loading = document.getElementById('loading');
loading.style.opacity = '0';
setTimeout(() => loading.remove(), 950);
}, 400);
addEventListener('resize', () => {
camera.aspect = innerWidth / innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(innerWidth, innerHeight);
composer.setSize(innerWidth, innerHeight);
});
</script>
</body>
</html>
-
ink 08-13 16:5211楼用gpt5.6sol中档生成的,效果也还行,就是太暗了

-
role_c 08-13 17:0012楼佬,你这个跑的时候,思维链是什么样的
-
小杨 08-13 17:0313楼
gpt生成的看起来还不错 -
道友 楼主 08-13 17:0314楼

这样的。我看不懂有什么区别。
-
传奇睡觉王 08-13 17:1815楼
GLM 5.2
* 帖子来源Linux.do
附近帖子
- ↑计算机专业的 gay 很多吗
- ↑谁能告诉我今天A\ 14点暴跌的原因?
- ↑nao榜更新了dsp和gork4.6
- ↑grokfree 使用成本估算
- ↑求助关于工作、租房、买车的问题
- 📍 deepseekV4 pro 生成的3D镂空机械表,这是什么水平?
- ↓V4P 现在可以稳定输出 repeat Nameeee了
- ↓有没有能用DeepSeek V4 Pro或flash的国模中转
- ↓1级新人报道~时隔2年,终于有了账号,注册时间8月13日
- ↓有没有可能用平板代替电脑,重活和 AI 都跑在云端
- ↓抖音提现羊毛