当前位置: 首页 > 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屏幕坐标

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

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

http://www.xdnf.cn/news/88507.html

相关文章:

  • usb2.0的硬件知识(一)
  • 查看MySql操作日志
  • 布隆过滤器的应用
  • 《Operating System Concepts》阅读笔记:p764-p766
  • 【Axure视频教程】不透明度函数
  • 以下是一个基于 ESP32 - S3 实现消息队列收发测试的 C 例程
  • crontab 定时备份 mysql 数据库
  • CF思维题(cf round 1019 div.2 b题)
  • ADS基本操作之S参数仿真
  • 如何高效优化复杂的SQL查询:以项目发布管理为例
  • Java知识大纲
  • 内存管理之文件内存映射(mmap):外存(磁盘/flash)的文件映射到应用层(跨越内核层)
  • 解析芯片低功耗设计的底层逻辑与实现方法
  • 最新项目笔记
  • Java的反射机制(曼波超易懂图文版)
  • 一洽智能硬件行业解决方案探索与实践
  • 从零开始学Python游戏编程33-指令模式2
  • AI大模型-window系统CPU版安装anaconda以及paddle详细步骤-亲测有效
  • c++STL——stack、queue、priority_queue的模拟实现
  • JDK安装超详细步骤
  • c#操作excel
  • Codeforces Round 1019 (Div. 2)(A-D)
  • 【线段树】P10381 「HOI R1」杂赛选比|普及+
  • SpringbootWeb开发(注解和依赖配置)
  • Sqlserver安全篇之_Sqlcmd命令使用windows域账号认证sqlserver遇到问题如何处理的案例
  • 基于STM32、HAL库的MCP4018T数字电位器驱动程序设计
  • 第5章-1 优化服务器设置
  • 08_Docker Portainer可视化管理
  • Kafka 面试,java实战贴
  • Java中常见API的分类概述及示例