副标题:从 4D 几何到 Three.js 渲染,再到 Gemini 2.5 Pro 与 Claude 3.7 Sonnet 的代码生成能力对比
关键词:四维可视化、Tesseract、JavaScript、Three.js、AI 编程、高维几何、物理模拟
引言:为什么要做“四维弹跳球”?
最近,Composio 发布了一篇极具启发性的评测文章:《Gemini 2.5 Pro vs. Claude 3.7 Sonnet: Coding Comparison》。其中一项测试任务令人眼前一亮:
“创建一个 JavaScript 脚本,可视化一个球在旋转的 4D 超立方体(Tesseract)内部弹跳。当球碰撞到某一面时,高亮该面以表示撞击。”
这不仅是一个炫酷的图形学 Demo,更是对 AI 模型在高维空间理解、物理逻辑建模、前端工程能力三重维度上的极限挑战。
本文将带你:
- 理解四维超立方体的数学本质;
- 掌握如何将其投影到三维并渲染;
- 实现球的运动与碰撞检测;
- 分析 Gemini 2.5 Pro 与 Claude 3.7 Sonnet 生成的代码差异;
- 提供可运行的完整实现方案。
一、什么是四维超立方体(Tesseract)?
1.1 从 0D 到 4D 的类比
| 维度 | 名称 | 构成方式 |
|---|---|---|
| 0D | 点 | 无 |
| 1D | 线段 | 两个点连接 |
| 2D | 正方形 | 两条平行线段连接 |
| 3D | 立方体 | 两个平行正方形连接 |
| 4D | 超立方体 | 两个平行立方体在第四维连接 |
因此,一个 Tesseract 由 16 个顶点、32 条边、24 个面、8 个体(立方体胞) 构成。
1.2 顶点坐标生成
所有顶点坐标为 (±1, ±1, ±1, ±1) 的组合,共 $2^4 = 16$ 个点。
例如:
- (1, 1, 1, 1)
- (-1, 1, -1, 1)
- ...
1.3 如何“看到”四维?
人类只能感知三维空间,因此需通过投影将 4D 物体降维到 3D,再用 WebGL 渲染到 2D 屏幕。
常用方法:透视投影(Perspective Projection)
公式如下(设观察点在 w 轴上,距离为 d):
$$ (x', y', z') = \left( \frac{d \cdot x}{d + w}, \frac{d \cdot y}{d + w}, \frac{d \cdot z}{d + w} \right) $$
当 w 增大,物体看起来“远离”;当 w 减小,物体“靠近”。
二、整体架构设计
我们将系统分为四个模块:
┌───────────────────────┐
│ Animation Loop │ ← requestAnimationFrame
└──────────┬────────────┘
↓
┌───────────────────────┐
│ 4D → 3D Projection │ ← project4Dto3D()
└──────────┬────────────┘
↓
┌───────────────────────┐
│ Physics & Collision │ ← updateBall(), checkCollision()
└──────────┬────────────┘
↓
┌───────────────────────┐
│ Three.js Rendering │ ← scene, camera, mesh
└───────────────────────┘
三、核心实现细节
3.1 构建 Tesseract 的几何结构
我们使用 Three.js 的 BufferGeometry 手动构建 8 个立方体(每个对应一个 w=±1 或 x=±1 等的子集)。
function createTesseract() {
const vertices4D = [];
for (let x of [-1, 1])
for (let y of [-1, 1])
for (let z of [-1, 1])
for (let w of [-1, 1])
vertices4D.push({ x, y, z, w });
// 定义 8 个立方体胞(每个固定一个坐标符号)
const cells = [
{ axis: 'w', sign: 1 },
{ axis: 'w', sign: -1 },
{ axis: 'x', sign: 1 },
{ axis: 'x', sign: -1 },
{ axis: 'y', sign: 1 },
{ axis: 'y', sign: -1 },
{ axis: 'z', sign: 1 },
{ axis: 'z', sign: -1 }
];
const meshes = [];
for (const cell of cells) {
const points3D = vertices4D
.filter(v => v[cell.axis] === cell.sign)
.map(v => project4Dto3D(v));
// 创建线框立方体(简化表示)
const geometry = new THREE.EdgesGeometry(new THREE.BoxGeometry(2, 2, 2));
const material = new THREE.LineBasicMaterial({
color: 0x4488ff,
transparent: true,
opacity: 0.8
});
const line = new THREE.LineSegments(geometry, material);
// 初始位置设为原点,后续在动画中动态更新
meshes.push({ line, originalVertices: vertices4D.filter(v => v[cell.axis] === cell.sign), cell });
}
return meshes;
}💡 注意:实际中需动态更新每个立方体的位置和旋转,因为整个 Tesseract 在 4D 中旋转。
3.2 4D 旋转变换
在 4D 中,旋转发生在平面上(如 XY、ZW、XW 等)。我们选择绕 XY 平面 和 ZW 平面 同时旋转:
function rotate4D(point, angleXY, angleZW) {
const { x, y, z, w } = point;
// XY 平面旋转
const x1 = x * Math.cos(angleXY) - y * Math.sin(angleXY);
const y1 = x * Math.sin(angleXY) + y * Math.cos(angleXY);
// ZW 平面旋转
const z1 = z * Math.cos(angleZW) - w * Math.sin(angleZW);
const w1 = z * Math.sin(angleZW) + w * Math.cos(angleZW);
return { x: x1, y: y1, z: z1, w: w1 };
}每帧更新旋转角度,实现流畅动画。
3.3 球的运动与碰撞检测
球的状态
const ball = {
pos: { x: 0, y: 0, z: 0, w: 0 }, // 4D 位置
vel: { x: 0.02, y: 0.03, z: -0.01, w: 0.015 }, // 4D 速度
radius: 0.2
};4D → 3D 投影函数
function project4Dto3D(p, distance = 3) {
const factor = distance / (distance + p.w);
return new THREE.Vector3(p.x * factor, p.y * factor, p.z * factor);
}碰撞检测(简化版)
由于 Tesseract 由 8 个轴对齐立方体组成,我们对每个立方体做 AABB 碰撞(在 3D 投影空间中近似):
function checkCollision(ball3D, cellVertices3D, radius) {
// 计算当前 cell 的包围盒
let minX = Infinity, maxX = -Infinity;
let minY = Infinity, maxY = -Infinity;
let minZ = Infinity, maxZ = -Infinity;
for (const v of cellVertices3D) {
minX = Math.min(minX, v.x); maxX = Math.max(maxX, v.x);
minY = Math.min(minY, v.y); maxY = Math.max(maxY, v.y);
minZ = Math.min(minZ, v.z); maxZ = Math.max(maxZ, v.z);
}
// 简化:判断球心是否进入包围盒扩展区域
if (
ball3D.x + radius > minX &&
ball3D.x - radius < maxX &&
ball3D.y + radius > minY &&
ball3D.y - radius < maxY &&
ball3D.z + radius > minZ &&
ball3D.z - radius < maxZ
) {
return true;
}
return false;
}碰撞响应
- 反转对应维度的速度(如撞到 w=1 面,则
vel.w *= -1); - 高亮该立方体 200ms。
// 示例:处理 w 轴碰撞
if (ball.pos.w > 0.9 && ball.vel.w > 0) {
ball.vel.w *= -1;
highlightCell('w', 1);
}3.4 渲染与动画循环
使用 Three.js 标准流程:
// 初始化场景
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x0a0a1a);
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.z = 5;
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// 创建对象
let tesseract = createTesseract();
tesseract.forEach(t => scene.add(t.line));
const ballGeometry = new THREE.SphereGeometry(0.1, 16, 16);
const ballMaterial = new THREE.MeshBasicMaterial({ color: 0xff5555 });
const ballMesh = new THREE.Mesh(ballGeometry, ballMaterial);
scene.add(ballMesh);
// 高亮管理
const highlights = new Map();
function animate() {
requestAnimationFrame(animate);
const time = Date.now() * 0.001;
const angleXY = time * 0.5;
const angleZW = time * 0.3;
// 更新球位置
ball.pos.x += ball.vel.x;
ball.pos.y += ball.vel.y;
ball.pos.z += ball.vel.z;
ball.pos.w += ball.vel.w;
// 边界反弹(简化)
if (Math.abs(ball.pos.x) > 0.95) ball.vel.x *= -1;
if (Math.abs(ball.pos.y) > 0.95) ball.vel.y *= -1;
if (Math.abs(ball.pos.z) > 0.95) ball.vel.z *= -1;
if (Math.abs(ball.pos.w) > 0.95) ball.vel.w *= -1;
// 投影球到 3D
const ball3D = project4Dto3D(ball.pos);
ballMesh.position.copy(ball3D);
// 更新 Tesseract 每个立方体
tesseract.forEach(t => {
const rotated = t.originalVertices.map(v => rotate4D(v, angleXY, angleZW));
const projected = rotated.map(v => project4Dto3D(v));
// 计算中心位置(用于平移)
const center = new THREE.Vector3();
projected.forEach(p => center.add(p));
center.divideScalar(projected.length);
// 应用变换(此处简化:仅平移,实际应重建几何)
t.line.position.copy(center);
// 检查碰撞
if (checkCollision(ball3D, projected, ball.radius)) {
// 高亮逻辑(略)
t.line.material.color.set(0xffff00);
setTimeout(() => t.line.material.color.set(0x4488ff), 200);
}
});
renderer.render(scene, camera);
}
animate();
// 自适应窗口
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});四、AI 生成代码对比分析
| 项目 | Gemini 2.5 Pro | Claude 3.7 Sonnet |
|---|---|---|
| 是否完整实现需求 | ✅ 是 | ✅ 是(但加了颜色) |
| 4D 投影逻辑 | 正确使用透视除法 | 类似,但未明确注释 |
| 碰撞高亮 | 精准高亮被撞面 | 高亮整个立方体 |
| 代码结构 | 模块化,注释清晰 | 紧凑,但略显混乱 |
| 性能 | 流畅运行 | 流畅,但额外着色开销 |
| 创新性 | 严格遵循 prompt | 自主添加视觉增强 |
📌 关键发现:Gemini 更“听话”,Claude 更“有想法”。在工程交付场景,前者更可靠;在创意探索场景,后者可能带来惊喜。
可以在以下链接查看完整代码:
- Gemini 2.5 Pro 生成代码
- Claude 3.7 Sonnet 生成代码
五、如何运行这个 Demo?
- 访问 Gemini 2.5 Pro 生成的完整代码 Gist
- 复制全部内容,保存为
index.html - 使用本地服务器打开(避免 CORS):
npx serve
# 或
python3 -m http.server 8000- 打开浏览器访问
http://localhost:8000,即可看到球在不断变形的四维结构中弹跳!
六、延伸思考
- 能否实现真正的 4D 物理引擎(如 4D 弹性碰撞、引力)?
- 是否可用 WebGPU 替代 WebGL 以支持更大规模的高维模拟?
- AI 能否自动生成交互式参数调节面板(如滑块控制旋转轴)?
- 这类 Demo 对教育(如线性代数、拓扑学、计算机图形学)有何价值?
此外,随着上下文窗口突破百万 token(如 Gemini 2.5 Pro 的 1M context),AI 已能处理极其复杂的跨文件项目。未来,“自然语言编程”或许不再是幻想。