副标题:从 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 模型在高维空间理解、物理逻辑建模、前端工程能力三重维度上的极限挑战。

本文将带你:

  1. 理解四维超立方体的数学本质;
  2. 掌握如何将其投影到三维并渲染;
  3. 实现球的运动与碰撞检测;
  4. 分析 Gemini 2.5 Pro 与 Claude 3.7 Sonnet 生成的代码差异;
  5. 提供可运行的完整实现方案。

一、什么是四维超立方体(Tesseract)?

1.1 从 0D 到 4D 的类比

维度名称构成方式
0D
1D线段两个点连接
2D正方形两条平行线段连接
3D立方体两个平行正方形连接
4D超立方体两个平行立方体在第四维连接

因此,一个 Tesseract16 个顶点、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 ProClaude 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 已能处理极其复杂的跨文件项目。未来,“自然语言编程”或许不再是幻想。