当前位置: 首页 > news >正文

三维几何变换

一、学习目的

了解几何变换的意义
掌握三维基本几何变换的算法

二、学习内容

在本次试验中,我们实现透视投影和三维几何变换。我们首先定义一个立方体作为我们要进行变换的三维物体。

三、具体代码

(1)算法实现
// 获取Canvas元素
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');// 定义立方体的顶点(8个顶点,每个顶点有x,y,z坐标)
// 定义一个单位立方体,中心在原点
let cube = [[-0.5, -0.5, -0.5], // 0: 左下后[0.5, -0.5, -0.5],  // 1: 右下后[0.5, 0.5, -0.5],   // 2: 右上后[-0.5, 0.5, -0.5],  // 3: 左上后[-0.5, -0.5, 0.5],  // 4: 左下前[0.5, -0.5, 0.5],   // 5: 右下前[0.5, 0.5, 0.5],    // 6: 右上前[-0.5, 0.5, 0.5]    // 7: 左上前
];// 定义立方体的边(12条边,每条边连接两个顶点)
const edges = [[0, 1], [1, 2], [2, 3], [3, 0], // 后面[4, 5], [5, 6], [6, 7], [7, 4], // 前面[0, 4], [1, 5], [2, 6], [3, 7]  // 连接前后面的边
];// 保存原始立方体顶点
const originalCube = JSON.parse(JSON.stringify(cube));// 变换参数
let scale = 100; // 缩放因子
let offsetX = canvas.width / 2;
let offsetY = canvas.height / 2;
let distance = 5; // 观察点到投影平面的距离// 绘制坐标轴
function drawAxes() {const axisLength = 150;// X轴ctx.beginPath();ctx.strokeStyle = '#e74c3c';ctx.lineWidth = 2;ctx.moveTo(offsetX, offsetY);ctx.lineTo(offsetX + axisLength, offsetY);ctx.stroke();// Y轴ctx.beginPath();ctx.strokeStyle = '#3498db';ctx.moveTo(offsetX, offsetY);ctx.lineTo(offsetX, offsetY - axisLength);ctx.stroke();// Z轴ctx.beginPath();ctx.strokeStyle = '#f1c40f';ctx.moveTo(offsetX, offsetY);ctx.lineTo(offsetX - axisLength * 0.7, offsetY + axisLength * 0.7);ctx.stroke();// 添加轴标签ctx.font = '14px Arial';ctx.fillStyle = '#e74c3c';ctx.fillText("X", offsetX + axisLength + 5, offsetY + 5);ctx.fillStyle = '#3498db';ctx.fillText("Y", offsetX - 15, offsetY - axisLength - 5);ctx.fillStyle = '#f1c40f';ctx.fillText("Z", offsetX - axisLength * 0.7 - 15, offsetY + axisLength * 0.7 + 15);
}// 透视投影函数
function perspectiveProjection(point) {// 简单的透视投影计算let z = point[2] + distance;let factor = distance / z;// 计算投影后的2D坐标let x = point[0] * factor * scale + offsetX;let y = -point[1] * factor * scale + offsetY; // 翻转Y轴以匹配屏幕坐标return [x, y];
}// 绘制立方体
function drawCube() {ctx.clearRect(0, 0, canvas.width, canvas.height);// 绘制坐标轴drawAxes();// 进行投影并绘制立方体的边ctx.beginPath();ctx.strokeStyle = 'rgb(0, 255, 0)'; // 使用RGB格式的绿色ctx.lineWidth = 3;for (let edge of edges) {const [p1Index, p2Index] = edge;const p1Projected = perspectiveProjection(cube[p1Index]);const p2Projected = perspectiveProjection(cube[p2Index]);ctx.moveTo(p1Projected[0], p1Projected[1]);ctx.lineTo(p2Projected[0], p2Projected[1]);}ctx.stroke();
}// 平移变换
function translate(dx, dy, dz) {for (let i = 0; i < cube.length; i++) {cube[i][0] += dx;cube[i][1] += dy;cube[i][2] += dz;}drawCube();
}// 缩放变换
function scaleTransform(sx, sy, sz) {for (let i = 0; i < cube.length; i++) {cube[i][0] *= sx;cube[i][1] *= sy;cube[i][2] *= sz;}drawCube();
}// 旋转矩阵计算
function rotateX(angle) {const cos = Math.cos(angle);const sin = Math.sin(angle);for (let i = 0; i < cube.length; i++) {const y = cube[i][1];const z = cube[i][2];cube[i][1] = y * cos - z * sin;cube[i][2] = y * sin + z * cos;}drawCube();
}function rotateY(angle) {const cos = Math.cos(angle);const sin = Math.sin(angle);for (let i = 0; i < cube.length; i++) {const x = cube[i][0];const z = cube[i][2];cube[i][0] = x * cos + z * sin;cube[i][2] = -x * sin + z * cos;}drawCube();
}function rotateZ(angle) {const cos = Math.cos(angle);const sin = Math.sin(angle);for (let i = 0; i < cube.length; i++) {const x = cube[i][0];const y = cube[i][1];cube[i][0] = x * cos - y * sin;cube[i][1] = x * sin + y * cos;}drawCube();
}// 重置立方体到初始状态
function resetCube() {cube = JSON.parse(JSON.stringify(originalCube));drawCube();
}// 添加按钮事件监听
document.getElementById('rotateX').addEventListener('click', () => rotateX(Math.PI / 18));
document.getElementById('rotateY').addEventListener('click', () => rotateY(Math.PI / 18));
document.getElementById('rotateZ').addEventListener('click', () => rotateZ(Math.PI / 18));
document.getElementById('translate').addEventListener('click', () => translate(0.1, 0, 0));
document.getElementById('scale').addEventListener('click', () => scaleTransform(1.1, 1.1, 1.1));
document.getElementById('reset').addEventListener('click', resetCube);// 初始化 - 绘制一个旋转了的立方体以便更好地观察
rotateX(Math.PI / 6);
rotateY(Math.PI / 4);
scaleTransform(1.5, 1.5, 1.5);
drawCube();
(2)前端HTML页面
<!DOCTYPE html>
<html>
<head><title>三维几何变换与透视投影</title><style>body {display: flex;flex-direction: column;align-items: center;background-color: #f5f5f5;font-family: Arial, sans-serif;padding: 20px;margin: 0;min-height: 100vh;}h1 {color: #2c3e50;margin-bottom: 30px;text-align: center;}canvas {border: 2px solid #34495e;border-radius: 8px;background-color: white;box-shadow: 0 4px 8px rgba(0,0,0,0.1);margin-bottom: 20px;}.controls {display: flex;gap: 10px;flex-wrap: wrap;justify-content: center;max-width: 600px;padding: 15px;background-color: white;border-radius: 8px;box-shadow: 0 2px 4px rgba(0,0,0,0.05);}button {padding: 12px 24px;border: none;border-radius: 5px;background-color: #2ecc71;color: white;cursor: pointer;transition: all 0.3s ease;font-size: 14px;font-weight: bold;text-transform: uppercase;letter-spacing: 1px;}button:hover {background-color: #27ae60;transform: translateY(-2px);box-shadow: 0 2px 4px rgba(0,0,0,0.1);}button#reset {background-color: #e74c3c;}button#reset:hover {background-color: #c0392b;}.container {max-width: 800px;width: 100%;margin: 0 auto;padding: 20px;}</style>
</head>
<body><div class="container"><h1>三维几何变换演示</h1><canvas id="canvas" width="600" height="400"></canvas><div class="controls"><button id="rotateX">绕X轴旋转</button><button id="rotateY">绕Y轴旋转</button><button id="rotateZ">绕Z轴旋转</button><button id="translate">平移</button><button id="scale">缩放</button><button id="reset">重置</button></div></div><script src="3dTransform.js"></script>
</body>
</html> 

四、运行结果

五、项目简介

# 3D几何变换演示

## 项目概述

这是一个基于Canvas的3D几何变换演示项目,展示了基本的3D变换操作:旋转、平移和缩放。项目使用JavaScript实现,通过透视投影将3D立方体渲染到2D画布上。

## 主要功能

- **旋转**:支持绕X轴、Y轴、Z轴旋转

- **平移**:沿X轴方向移动立方体

- **缩放**:均匀缩放立方体大小

- **重置**:恢复立方体到初始状态

## 代码结构

- `cube`数组定义了立方体的8个顶点坐标

- `edges`数组定义了连接顶点的12条边

- 主要变换函数:

  - `rotateX/Y/Z()` - 旋转

  - `translate()` - 平移

  - `scaleTransform()` - 缩放

  - `resetCube()` - 重置

## 使用方法

1. 确保HTML文件中包含对应按钮

2. 点击按钮触发相应变换:

   - 旋转X/Y/Z - 绕对应轴旋转

   - 平移 - 沿X轴移动

   - 缩放 - 均匀放大

   - 重置 - 恢复初始状态

3. 初始状态立方体会自动旋转一定角度以便观察

## 技术要点

- 使用透视投影将3D坐标转换为2D屏幕坐标

- 实时重绘立方体实现动画效果

- 保留原始顶点数据用于重置功能

相关文章:

  • 修改element UI 分页组件样式(解决样式不生效问题)
  • Java多线程编程初阶指南
  • 【实战】oninput 文本框输入实时查询防抖机制实现
  • 数字IC低功耗设计——基础概念和低功耗设计方法
  • 出现了锁等待或死锁现象怎么办?乐观锁?分布式锁了解一下?
  • 前端笔记-Vue3(中)
  • 输入框仅支持英文、特殊符号、全角自动转半角 vue3
  • Sqlserver安全篇之_Sqlcmd命令使用windows域账号认证sqlserver遇到问题如何处理的案例
  • JVM考古现场(二十四):逆熵者·时间晶体的永恒之战
  • 乐视系列玩机---乐视1 x600系列线刷救砖以及刷写第三方twrp 卡刷第三方固件步骤解析
  • 【AI News | 20250422】每日AI进展
  • Java 静态内部类面试题与高质量答案合集
  • 华为仓颉编程语言基础概述
  • 【漫话机器学习系列】215.处理高度不平衡数据策略(Strategies For Highly Imbalanced Classes)
  • 性能比拼: Redis vs Dragonfly
  • 服装印花/印烫环节计算机视觉应用设计方案
  • STL C++详解——priority_queue的使用和模拟实现 堆的使用
  • jenkins pipeline ssh协议报错处理
  • 【MCP Node.js SDK 全栈进阶指南】初级篇(4):MCP工具开发基础
  • 【MCP Node.js SDK 全栈进阶指南】初级篇(3):MCP资源开发基础
  • 老人刷老年卡乘车被要求站着?公交公司致歉:涉事司机停职
  • “五一”假期国内出游3.14亿人次,国内游客出游总花费1802.69亿元
  • 杨德龙:取得长期投资胜利法宝,是像巴菲特一样践行价值投资
  • 伊朗公布新型弹道导弹,“萨德”系统无法拦截
  • 准80后遵义市自然资源局局长陈清松任仁怀市委副书记、代市长
  • 5月1日,全社会跨区域人员流动量完成33271.4万人次