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

《计算机图形学》第二课笔记-----二维变换的推导

前言:为什么这么突兀的把这一节内容放在了第二课,第一是因为我急于求成,第二是因为这一章节太重要了,这几乎是二维三维变换的最核心的东西,理解了这一章节内容,后面的就会像打通了任督二脉一样,so,那让我们开始吧,我们只用初中的知识把这一章说清楚

所谓的二维变换核心的东西其实就是求一个点绕一个点旋转后的位置

让我们对这段话进行一些约束,在一个去掉了Z轴的笛卡尔坐标系内,让我们再大白话点,在一个平面坐标系内,让我们再大白话点,就是小时候上数学课时候老师在黑板上化的那个一横一竖,上面一个箭头,前面一个箭头那种坐标系内
我们终于白话完了,正文开始,在一个坐标系内一个点绕着原点o旋转一定的度数,旋转到新的一个点,求这个点在这个坐标系内的位置,也就是新的点位的x’的长度和y’的长度。
结合下图描述了一个已知的点[x,y],沿原点o旋转了β度到达了新的点[x’,y’],求[x’,y’]的位置
我们需要了解的前提是[x,y]是已知的
那么x’也可以写成r*cos(α+β),因为我们是绕原点旋转,等于在绕原点画圆,so,每一个半径都应该是相等的,记住这个前提,后面要用。

在这里插入图片描述

x ′ = r ∗ c o s ( α + β ) x'=r*cos(α+β) x=rcos(α+β
上面这个式子应该很好理解,如果不理解可以只补一下三角函数的cos 和 sin即可

接下来我们展开这个式子,有人可能会在这里卡住一下,但这其实都是固定推导您只需复制展开前的式子问下一ai,他会说的比我清楚多了,总而言之如果您不想深究,不用理会为什么展开以后是这样子,直接过即可
x ′ = r ∗ c o s α ∗ c o s β − r ∗ s i n α ∗ s i n β x'=r*cosα*cosβ-r*sinα*sinβ x=rcosαcosβrsinαsinβ

当我们观察展开的式子,这个时候神奇的事情发生了,r*cosα,不就是 r x r r\frac{x}{r} rrx,约分以后不就是x么
我们再观察r*sinα,那不就是 r y r r\frac{y}{r} rry,约分以后那不就是y么

然而x,y又是已知的,那不就是用已知的x,y去乘我们旋转的β角的cos和sin么,至此答案已经很清晰了,可以说这篇博客要说的已经说完了,您仅需初中知识就可以理解这一切,后续的剩下的推导也只是上述思考过程的重复,当然我们还是要写完这一切。

x ′ = x ∗ c o s β − y ∗ s i n β x'=x*cosβ-y*sinβ x=xcosβysinβ
y ′ = x ∗ s i n β + y ∗ c o s β y'=x*sinβ+y*cosβ y=xsinβ+ycosβ

最后我们把他写成矩阵的形式就是下面的式子,我们需要注意的是矩阵乘法并不满足交换律,so,我们不能调换位置

[ c o s β − s i n β s i n β c o s β ] [ x y ] \begin{bmatrix} cosβ & -sinβ \\ sinβ & cosβ \\ \end{bmatrix}\begin{bmatrix} x\\ y\\ \end{bmatrix} [cosβsinβsinβcosβ][xy]

当然我们需要注意,当前我们讨论的是逆时针旋转这种情况,顺时针的推到过程在下下张图

那么剩下的顺时针推导我们就不写这么详细了,因为一切已经不言自明了,下一节我们就去到代码层面去实现它,平移我们也就不在此解释了,因为那些比这个容易理解的多。

在这里插入图片描述

原因如上,顺时针的推导就不赘述了

在这里插入图片描述

现在我们来到代码层面,UI框架选择c++ qt5,因为我更熟悉这个框架,而且如果我们仅仅知识为了演示二维变换的推导,qt5足够了,主要原因还是我足够熟悉。不用opengl是因为我们像尽其可能的展示细节,而不是直接调用显卡为我们实现好的接口。

头文件

#ifndef TWOCUBE_1_H // 防止头文件被重复包含
#define TWOCUBE_1_H#include <QWidget> // 包含QWidget类,用于创建窗口
#include <QTimer>  // 包含QTimer类,用于定时器功能// TwoCube_1类继承自QWidget,用于创建自定义窗口
class TwoCube_1: public QWidget
{Q_OBJECT // 启用Qt的元对象系统,支持信号和槽机制public:// 构造函数,参数为父窗口指针TwoCube_1(QWidget* parent);// 析构函数~TwoCube_1();protected:// 重写paintEvent函数,用于处理窗口的绘制事件void paintEvent(QPaintEvent* event) override;private:QList<QPointF> rectPoints;  // 用于存储矩形的顶点坐标(2D坐标)int angle = 0;              // 旋转角度,初始化为0float rWidth = 300;         // 矩形的宽度float rHeight = 180;        // 矩形的高度
};#endif // TWOCUBE_1_H // 结束头文件定义

cpp文件

#include "twocube_1.h"
#include <QPainter>
#include <QDebug>
#include <cmath>// 构造函数,初始化窗口和矩形点集
TwoCube_1::TwoCube_1(QWidget* parent): QWidget(parent)
{// 设置窗口的最小大小为800x800setMinimumSize(800, 800);// 初始化矩形的四个顶点坐标rectPoints.append(QPointF(0,0));           // 左上角rectPoints.append(QPointF(0,rHeight));      // 左下角rectPoints.append(QPointF(rWidth,rHeight)); // 右下角rectPoints.append(QPointF(rWidth,0));       // 右上角// 创建定时器用于动画效果QTimer* timer = new QTimer(this);// 连接定时器的timeout信号到lambda表达式,每16ms触发一次connect(timer, &QTimer::timeout, this, [this]() {angle -= 1;  // 每次更新角度减少1度,顺时针旋转update();    // 触发重绘事件});timer->start(16); // 定时器每16ms触发一次,约60fps
}// 析构函数
TwoCube_1::~TwoCube_1()
{// 析构函数为空,没有需要释放的资源
}// 重写paintEvent函数,处理窗口的绘制事件
void TwoCube_1::paintEvent(QPaintEvent *event)
{QPainter painter(this);  // 创建QPainter对象,用于绘制// 将绘制原点移动到窗口中心painter.translate(width() / 2, height() / 2);// 设置画笔颜色为黑色,线宽为1painter.setPen(QPen(Qt::black, 1));// 绘制X轴和Y轴painter.drawLine(-width() / 2, 0, width(), 0);  // X轴painter.drawLine(0, -height() / 2, 0, height()); // Y轴// 用于存储旋转后的矩形顶点QList<QPointF> tempRectPoints;// 对矩形的每个顶点进行旋转变换for (int i = 0; i < rectPoints.length(); i++){float x = rectPoints[i].x();  // 获取当前顶点的X坐标float y = rectPoints[i].y();  // 获取当前顶点的Y坐标// 计算旋转后的新坐标float newX = x * cos(angle * M_PI / 180) + y * sin(angle * M_PI / 180);float newY = -x * sin(angle * M_PI / 180) + y * cos(angle * M_PI / 180);// 更新旋转后的坐标x = newX;y = newY;// 将旋转后的点添加到临时列表中QPointF tempPoint(x, y);tempRectPoints.append(tempPoint);}// 设置画笔颜色为绿色,线宽为5painter.setPen(QPen(Qt::green, 5));// 绘制旋转后的矩形painter.drawLine(tempRectPoints[0], tempRectPoints[1]);  // 左边painter.drawLine(tempRectPoints[1], tempRectPoints[2]);  // 下边painter.drawLine(tempRectPoints[2], tempRectPoints[3]);  // 右边painter.drawLine(tempRectPoints[3], tempRectPoints[0]);  // 上边
}

最后放上一张效果图
我们演示使用的是顺时针旋转,在一个定时器内每个16ms角度自身减1,重新触发绘制,这里一定要注意,一定是角度递减才是顺时针旋转,才能套用顺时针旋转的算法,让我们把代码摘出来再捋一遍

这行代码代表顺时针旋转

angle -= 1;  // 每次更新角度减少1度,顺时针旋转

这段代码就是套用了顺时针旋转的算法,里面做了角度转弧度的处理,因为大部分编程语言只认弧度

// 计算旋转后的新坐标float newX = x * cos(angle * M_PI / 180) + y * sin(angle * M_PI / 180);float newY = -x * sin(angle * M_PI / 180) + y * cos(angle * M_PI / 180);

在这里插入图片描述


http://www.mrgr.cn/news/94453.html

相关文章:

  • 《解锁Netlify:静态网站托管》:此文为AI自动生成
  • MATLAB 控制系统设计与仿真 - 26
  • 《基於Python的网络爬虫抓包技术研究与应用》
  • Java8的新特性
  • Python字符串高效优化策略:特定编码 -> Unicode码点 -> UTF-8(可自定义)
  • 序列化和反序列化TCP粘包问题
  • 《GitHub网路访问不稳定:解决办法》:此文为AI自动生成
  • 【原创】在高性能服务器上,使用受限用户运行Nginx,充当反向代理服务器[未完待续]
  • Java高级-04.反射-获取成员变量和方法对象并使用
  • QT中读取QSetting文件
  • Android 高版本 DownloadManager 封装工具类,支持 APK 断点续传与自动安装
  • C++ primer plus 类和对象下
  • VSCode C/C++ 开发环境完整配置及常见问题(自用)
  • 神经网络中常用语言特性(python)(待完善)
  • 软考计算机知识-流水线
  • 【AWS入门】2025 AWS亚马逊云科技账户注册指南
  • 解释VLA和具身智能之间的关系
  • 插入排序算法的SIMD优化
  • JavaScript基础篇:六、 函数基础
  • 模版题目的集合