三维几何变换

一、学习目的

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

二、学习内容

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

三、具体代码

(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屏幕坐标

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

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值