You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JavaScript实现简易第一人称3D相机及Canvas 3D矩形问题问询

问题解答:简易第一人称3D相机与Canvas 3D矩形实现

一、Canvas原生没有直接绘制3D矩形的函数

Canvas是2D绘图API,不存在原生的3D矩形绘制函数。所有3D视觉效果都需要通过透视投影计算,把3D空间坐标转换为2D屏幕坐标后,用2D绘图API(如beginPath()、lineTo())模拟实现。

二、实现支持3D参数的矩形绘制与移动

1. 核心:透视投影原理

3D点转2D屏幕坐标的基础公式(模拟人眼近大远小):

// 把3D坐标(x,y,z)转换为Canvas屏幕坐标
function projectPoint(x, y, z, camera) {
  const fov = 60; // 视角,控制视野宽窄
  const scale = Math.tan((fov * Math.PI / 180) / 2) * canvas.height;
  // 基于相机位置计算屏幕坐标
  const screenX = canvas.width/2 + ((x - camera.x) * scale) / (z - camera.z);
  const screenY = canvas.height/2 + ((y - camera.y) * scale) / (z - camera.z);
  return { x: screenX, y: screenY };
}

2. 自定义3D矩形绘制函数

接收x,y,depth(z),width,height参数,计算矩形四个顶点的3D坐标,转成2D后绘制:

function draw3DRect(x, y, z, width, height, camera) {
  // 定义矩形四个顶点的3D坐标
  const vertices = [
    { x, y, z },
    { x: x+width, y, z },
    { x: x+width, y: y+height, z },
    { x, y: y+height, z }
  ];

  // 把所有顶点转成2D屏幕坐标
  const screenVertices = vertices.map(v => projectPoint(v.x, v.y, v.z, camera));

  // 用2D API绘制矩形
  ctx.beginPath();
  ctx.moveTo(screenVertices[0].x, screenVertices[0].y);
  screenVertices.forEach(v => ctx.lineTo(v.x, v.y));
  ctx.closePath();
  ctx.strokeStyle = '#222';
  ctx.stroke();
  ctx.fillStyle = 'rgba(100, 200, 255, 0.6)';
  ctx.fill();
}

3. 实现矩形的水平/垂直移动

本质就是修改矩形的x(水平)或y(垂直)参数,在动画循环中重新计算投影并绘制。比如用键盘控制:

document.addEventListener('keydown', e => {
  const speed = 0.1;
  switch(e.key) {
    case 'ArrowLeft': rect.x -= speed; break;
    case 'ArrowRight': rect.x += speed; break;
    case 'ArrowUp': rect.y -= speed; break;
    case 'ArrowDown': rect.y += speed; break;
  }
});

三、你的代码问题修复

你提供的代码存在几个关键问题:

  • 矩阵变换逻辑错误:用了错误的三角函数组合,没有遵循正确的3D投影/旋转公式
  • 变量不规范:x2、y2等未用let/const声明,容易造成全局污染
  • 无正确透视计算:直接加减z值无法模拟3D深度效果
  • 错误API调用:ctx.reset()不是Canvas标准方法,应该用ctx.clearRect()清除画布

下面是修正后的完整示例(包含相机、矩形绘制和移动):

const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;

// 相机参数
const camera = { x: 0, y: 0, z: 5 };
// 矩形参数
const rect = { x: 0, y: 0, z: 2, width: 2, height: 2, speed: 0.1 };

// 3D转2D投影函数
function projectPoint(x, y, z) {
  const fov = 60;
  const scale = Math.tan((fov * Math.PI / 180) / 2) * canvas.height;
  const screenX = canvas.width/2 + ((x - camera.x) * scale) / (z - camera.z);
  const screenY = canvas.height/2 + ((y - camera.y) * scale) / (z - camera.z);
  return { x: screenX, y: screenY };
}

// 绘制3D矩形
function draw3DRect() {
  const vertices = [
    { x: rect.x, y: rect.y, z: rect.z },
    { x: rect.x+rect.width, y: rect.y, z: rect.z },
    { x: rect.x+rect.width, y: rect.y+rect.height, z: rect.z },
    { x: rect.x, y: rect.y+rect.height, z: rect.z }
  ];
  const screenVertices = vertices.map(v => projectPoint(v.x, v.y, v.z));

  ctx.beginPath();
  ctx.moveTo(screenVertices[0].x, screenVertices[0].y);
  screenVertices.forEach(v => ctx.lineTo(v.x, v.y));
  ctx.closePath();
  ctx.stroke();
  ctx.fillStyle = 'rgba(100,200,255,0.6)';
  ctx.fill();
}

// 键盘控制移动
document.addEventListener('keydown', e => {
  switch(e.key) {
    case 'ArrowLeft': rect.x -= rect.speed; break;
    case 'ArrowRight': rect.x += rect.speed; break;
    case 'ArrowUp': rect.y -= rect.speed; break;
    case 'ArrowDown': rect.y += rect.speed; break;
  }
});

// 动画循环
function animate() {
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  draw3DRect();
  requestAnimationFrame(animate);
}
animate();

四、简易第一人称3D相机实现思路

第一人称相机的核心是让相机跟随用户输入移动/旋转:

  • 移动:通过键盘控制camera.x、camera.y、camera.z的增减,模拟前进/后退/左右移动
  • 旋转:通过鼠标移动计算相机的俯仰角(pitch)和偏航角(yaw),对所有3D点先做旋转变换再投影,模拟转头效果
    示例旋转逻辑:
    // 绕Y轴旋转(偏航,左右转头)
    function rotateY(x, z, yaw) {
      const cos = Math.cos(yaw);
      const sin = Math.sin(yaw);
      return {
        x: x * cos - z * sin,
        z: x * sin + z * cos
      };
    }
    

内容的提问来源于stack exchange,提问作者mastersam

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 03:25:01