1.6.1 变换

我们要想改变物体的位置,现有解决办法是,每一帧改变物体的顶点并且重配置缓冲区从而使物体移动,但是这样太繁琐,更好的解决方式是使用矩阵(Matrix)来更好的变换(Transform)一个物体。

一、向量

向量是有方向的量,向量有一个方向(Direction)和大小(Magnitude,也叫强度或长度)。
数学家喜欢在字母上面加一横表示向量,比如说 。当用在公式中时它们通常是这样的:

1、向量与标量运算
标量只是一个数字(或者说是仅有一个分量的向量)。当把一个向量加、减、乘、除一个标量时,可以把向量的每个分量分别与这个标量进行运算。如下是向量与标量的加法运算:

2、向量取反
对一个向量取反会将其方向逆转,一个指向东北的向量取反后就指向西南方向了。在一个向量的每个分量前加负号就可以实现取反:

3、向量加减
向量的加法可以被定义为分量的相加,即将一个向量中的每个分量加上另一个向量对应的分量:

两个向量想加的结果向量是从第一个向量(v=(4,2))起点指向第二个向量(k=(1,2))的终点

向量的减法等于第一个向量加上第二个向量的相反向量:

两个向量相减会得到这两个向量指向位置的差:从第二个向量(v=(3,2))的终点指向第一个向量(v=(0.5,3.5))的终点,再把起点位置移动到(0,0)处,最终向量是(-2.5,1.5)

4、长度
使用勾股定理来获取向量的长度/大小,如果你把向量的x与y分量画出来,该向量会和x与y分量为边形成一个三角形:

因为两条边(x和y)是已知的,如果希望知道斜边的长度,可以直接通过勾股定理计算:

||v¯||表示向量的长度,例子中向量(4,2)的长度等于4.47:

有一个特殊类型的向量叫做单位向量(Unit Vector)。单位向量有一个特别的性质:长度是1。可以用任意向量的每个分量除以向量的长度得到它的单位向量:

这种方法叫做一个向量的标准化,单位向量头上有一个^样子的记号。

二、向量相乘

普通的乘法在向量上时没有定义的,因为它在视觉上没有意义。但是在相乘的时候有两种特定情况可以选择:一个是点乘,另一个是叉乘。
1、点乘
点乘是通过将对应分量逐个相乘,然后再把所有得积相加来计算的:

同时,两个向量的点乘等于它们的数乘结果乘以两个向量之间夹角的余弦值,公式如下:

它们之间的夹角记作 θ。当两个向量都是单位向量时,公式简化为:

现在点乘只定义了两个向量的夹角,0度的余弦值是1,90度的余弦值是0,使用点乘可以很容易测试两个向量是否是正交或平行(想了解更多关于正弦或余弦函数的知识,推荐看可汗学院的基础三角学视频: https://www.khanacademy.org/math/geometry-home/right-triangles-topic/intro-to-the-trig-ratios-geo/v/basic-trigonometry )。
也可以通过点乘的结果计算两个非单位向量的夹角,点乘的结果除以两个向量的长度之积,得到的结果就是夹角的余弦值,即cos θ:

2、叉乘
叉乘需要两个不平行向量作为输入,生成一个正交与两个输入向量的第三个向量。
如果输入的两个向量也是正交的,那么叉乘之后将会产生3个相互正交的向量:

叉乘公式如下:

三、矩阵

简单来说矩阵就是一个矩形的数字、符号或表达式数组。矩阵中每一项叫做矩阵的元素(Element)。
下面是一个2 × 3的矩阵:

矩阵可以通过(i,j)进行索引,i是行,j是列,这就是上面的矩阵叫做 2 × 3的矩阵的原因(2行3列,也叫做矩阵的维度)。
1、矩阵的加减
矩阵与标量之间的加减定义如下,标量值要加减到矩阵的每一个元素上:

矩阵与矩阵之间的加减就是两个矩阵对应元素的加减运算,只有同维度的矩阵才能加减:

2、矩阵的数乘
矩阵与标量的乘法是矩阵的每一个元素分别乘以该标量:

四、矩阵相乘

矩阵相乘有一些限制:
  • 只有当左侧矩阵的列数与右侧矩阵的行数相等时,才能相乘;
  • 矩阵相乘不遵守交换律,也就是说A·B≠B·A;
下面是两个2×2矩阵相乘的例子:

  • 首先用左侧矩阵的第1行的第1个数乘以右侧矩阵的第1列的第1个数加上左侧矩阵的第1行第2个数乘以右侧矩阵的第1列第2个数,得到的结果放在第1行第1列上;
  • 然后用左侧矩阵的第1行的第1个数乘以右侧矩阵的第2列的第1个数加上左侧矩阵的第1行第2个数乘以右侧矩阵的第2列第2个数,得到的结果放在第1行第2列上;
  • 然后用左侧矩阵的第2行的第1个数乘以右侧矩阵的第1列的第1个数加上左侧矩阵的第2行第2个数乘以右侧矩阵的第1列第2个数,得到的结果放在第2行第1列上;
  • 最后用左侧矩阵的第2行的第1个数乘以右侧矩阵的第2列的第1个数加上左侧矩阵的第2行第2个数乘以右侧矩阵的第2列第2个数,得到的结果放在第2行第2列上;
  • 结果得到一个维度是(n,m)的矩阵,n等于左侧矩阵的行数,m等于右侧矩阵的列数(n×k矩阵 乘以 k×m矩阵得到一个n×m矩阵)。
(想了解更多的矩阵知识,推荐看可汗学院的矩阵教程: https://www.khanacademy.org/math/algebra2/algebra-matrices)

五、矩阵与向量相乘

向量可以看成是N×1的矩阵,N表示向量分量的个数。如果我们有一个M×N矩阵,可以用这个矩阵乘以N×1向量,
因为这个矩阵的列数等于这个向量的行数,所以他们能相乘。
1、单位矩阵
在OpenGL中,由于某些原因我们通常使用4 ×4的变换矩阵,其中最重要的原因就是大部分的向量都是4分量的。
最简单的变换矩阵就是单位矩阵,单位矩阵是一个除了对角线以外都是0的N ×N矩阵。
下面可以看到这个变换矩阵使一个向量完全不变:

2、缩放矩阵
对一个向量进行缩放就是对这个向量的长度进行缩放,它的方向保持不变。
先尝试缩放向量 v ¯ = ( 3 , 2 ), 把向量沿着x轴缩放0.5,使它的宽度缩小为原来的二分之一, 沿着y轴把向量的高度缩放为原来的两倍,得到向量 s ¯

OpenGL通常在3D空间进行操作,对于2D的情况我们当做把z轴缩放1倍,这样z轴的值保持不变。
刚刚的缩放操作时, 每个轴的缩放因子都不一样,所以 是不均匀缩放;如果每个轴的缩放因子都一样就叫均匀缩放。
下图为向量(x,y,z)定义一个缩放矩阵:

S1、S2、S3分别表示对轴x、y、z的缩放倍数。注意,第四个缩放向量(w分量)仍然是1,这个w分量有其他用途。
3、位移矩阵
位移是在原来向量的基础上加上另一个向量从而获得一个不同位置的新向量的过程。
4 ×4矩阵上的第四列最上面的3个值用来实现向量的位移操作,位移矩阵定义如下:

齐次坐标:
向量的w分量也叫齐次坐标,想要从齐次向量得到3D向量,我们可以把x、y、z坐标分别除以w坐标。使用齐次坐标的好处:它允许我们在3D向量上进行移动(如果没有w分量我们不能位移向量),而且下一章我们会用w值创建3D视觉效果。
如果一个向量的齐次坐标是0,这个坐标就是方向向量,这个向量不能移动。
4、旋转矩阵
如果想了解旋转矩阵是如何构造出来的,推荐看可汗学院线性代数的视频: https://www.khanacademy.org/math/linear-algebra/matrix_transformations
2D或3D空间中旋转用角来表示。角可以是角度制或弧度制的,周角是360角度或2PI弧度。
大多数旋转函数需要用弧度制的角,角度制的角可以和弧度制相互转化:
  • 弧度制角度:角度 = 弧度 * (180.0f / PI);
  • 角度制弧度:弧度 = 角度 * (PI / 180.0f);
PI约等于3.14159265359。
转半圈会旋转360/2 = 180度,向右旋转1/5圈表示向右旋转360/5 = 72度。
在3D空间中旋转需要定义一个角和一个旋转轴,物体会沿着给定的旋转轴旋转特定角度。
使用三角学,给定一个角度,可以把一个向量变换为一个经过旋转的新向量,这通常使用一系列正弦和余弦函数进行巧妙的组合得到(如何生成旋转矩阵超出本教程的范围,直接看旋转矩阵即可)。
旋转矩阵在3D空间中每个单位轴上有不同定义,旋转角度用 θ表示,沿x轴旋转:

沿y轴旋转:

沿z轴旋转:

利用旋转矩阵可以把任意向量沿着一个单位轴进行旋转,也可以将多个矩阵复合,比如先沿着x轴旋转再沿着y轴旋转,但是这会导致万向节死锁(万向节死锁的知识推荐看这个视频: https://www.youtube.com/watch?v=zc8b2Jo7mno )。一个更好的模型是沿着任意的一个轴进行旋转,而不是一系列旋转矩阵进行复合,这样的矩阵是存在的,见下面的公式,其中 ( R x , R y , R z ) 代表任意旋转轴:

在数学上讨论如何生成这样的矩阵仍然超过本节内容,但是,这样一个矩阵也只是会极大地避免万向节死锁的问题,不能完全的解决 万向节死锁的问题。避免 万向节死锁的真正解决方案是使用四元数(关于四元数,推荐看这个: https://krasjet.github.io/quaternion/quaternion.pdf ),它不仅更安全,而且计算会更有效率。
5、矩阵的组合
使用矩阵进行变换的真正力量在于,根据矩阵之间的乘法,可以把多个变换组合到一个矩阵中。
假设有一个顶点(x,y,z),我们希望将其缩放2倍,然后位移(1,2,3)个单位,我们需要一个位移和缩放矩阵来完成这些变换,结果的变换矩阵像这样:

注意,当矩阵相乘时我们先写位移再写缩放变换,矩阵乘法是不遵守交换律的,这意味这它们的顺序很重要。当矩阵相乘时,在最右边的矩阵是第一个向量相乘的,所以你应该从右向左读这个乘法。建议在组合矩阵时先进行缩放操作,然后旋转,最后才是位移(位移一般要放在最后;假设现在需要旋转和位移,如果是先旋转再位移,旋转的中心就是物体的中点点; 如果是先位移再旋转,旋转的中心是原点 )。
最终的变换矩阵左乘向量得到以下结果:

这样,向量先缩放2倍,然后位移了(1,2,3)个单位。
上面的矩阵可以写成如下样式,这样的话向量会和第二个矩阵进行运算,运算结果再和第一个矩阵进行运算,这样的计算结果是先缩放再位移:

如果写成下面这样,计算结果是先位移再缩放(下一节的练习1中展示这种效果)

六、实践

更改程序,实现矩形位移和旋转功能。
更改顶点着色器代码,创建一个 uniform mat4 RotationMatrix变量,用于接收变换矩阵
#version 330 corelayout (location = 0) in vec3 aPos; //位置变量的属性位置值为0
layout (location = 1) in vec3 aColor; //颜色变量的属性位置值为1
layout (location = 2) in vec2 aTexture; //纹理变量的属性位置值为2out vec3 ourColor; //向片段着色器输出一个颜色坐标
out vec2 ourTexture; //向片段着色器输出一个纹理坐标uniform mat4 RotationMatrix; //变换矩阵void main()
{//矩阵与向量相乘,使向量进行旋转、缩放、位移等gl_Position = RotationMatrix * vec4(aPos, 1.0);ourColor = aColor;ourTexture = aTexture;
}

在myopenglwidget.h文件中创建定时器对象m_timer和定时器响应槽函数函数onTimeout

#ifndef MYOPENGLWIDGET_H
#define MYOPENGLWIDGET_H
#include <QOpenGLWidget>
#include <QOpenGLFunctions_3_3_Core>
#include <QOpenGLShaderProgram>
#include <QOpenGLTexture>
#include <QTimer>class MyOpenGLWidget : public QOpenGLWidget,QOpenGLFunctions_3_3_Core
{Q_OBJECTpublic:explicit MyOpenGLWidget(QWidget *parent = nullptr);~MyOpenGLWidget();protected:virtual void initializeGL();virtual void resizeGL(int w, int h);virtual void paintGL();void keyPressEvent(QKeyEvent *event);private slots:void onTimeout();private:QOpenGLShaderProgram m_shaderProgram;QOpenGLTexture *m_textureWall;QOpenGLTexture *m_textureSmile;QOpenGLTexture *m_textureSmall;float mixValue = 0.5;QTimer m_timer;
};#endif // MYOPENGLWIDGET_H

在myopenglwidget.h文件中构造函数进行定时器的信号与槽函数的绑定,并启动100ms的定时器;析构函数中停止定时器:

#include "myopenglwidget.h"
#include <QDebug>
#include <QKeyEvent>
#include <QTime>unsigned int VBO; //顶点缓冲对象
unsigned int VAO; //顶点数组对象
unsigned int EBO; //元素缓冲对象MyOpenGLWidget::MyOpenGLWidget(QWidget *parent) : QOpenGLWidget(parent)
{setFocusPolicy(Qt::StrongFocus);connect(&m_timer, &QTimer::timeout, this, &MyOpenGLWidget::onTimeout);m_timer.start(100); //100ms
}MyOpenGLWidget::~MyOpenGLWidget()
{if(m_timer.isActive())m_timer.stop();makeCurrent();glDeleteBuffers(1, &VBO);glDeleteVertexArrays(1, &VAO);glDeleteBuffers(1, &EBO);doneCurrent();
}void MyOpenGLWidget::onTimeout()
{update();
}void MyOpenGLWidget::initializeGL()
{//初始化OpenGL函数initializeOpenGLFunctions();//创建VBO,并赋予IDglGenBuffers(1, &VBO);//绑定VBO对象glBindBuffer(GL_ARRAY_BUFFER, VBO);//顶点数据float vertices[] = {//位置              //颜色             //纹理0.5f,  0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, //右上角0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, //右下角-0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, //左下角-0.5f,  0.5f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f //左上角};//把顶点数据复制到显存中glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);//创建VAO对象,并赋予IDglGenVertexArrays(1, &VAO);//绑定VAO对象glBindVertexArray(VAO);//创建EBO对象,并赋予IDglGenBuffers(1, &EBO);glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);unsigned int indices[] = {0, 1, 3, //第一个三角形1, 2, 3 //第二个三角形};glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);//位置属性glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0);glEnableVertexAttribArray(0); //开启VAO管理的第一个属性值//颜色属性glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));glEnableVertexAttribArray(1); //开启VAO管理的第二个属性值//纹理属性glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));glEnableVertexAttribArray(2); //开启VAO管理的第三个属性值//解绑VBOglBindBuffer(GL_ARRAY_BUFFER, 0);//解绑VAOglBindVertexArray(0);//创建一个程序对象m_shaderProgram.addShaderFromSourceFile(QOpenGLShader::Vertex,":/shaders/shapes.vert");m_shaderProgram.addShaderFromSourceFile(QOpenGLShader::Fragment,":/shaders/shapes.frag");bool success = m_shaderProgram.link();if(!success)qDebug()<<"ERR:" << m_shaderProgram.log();m_shaderProgram.bind();m_shaderProgram.setUniformValue("vertexColor", 0.0, 1.0, 0.0, 1.0);m_textureWall = new QOpenGLTexture(QImage(":/images/wall.jpg").mirrored()); //mirrored消除镜像m_shaderProgram.setUniformValue("textureWall", 0); //把纹理单元传给片段着色器中的采样器m_textureSmile = new QOpenGLTexture(QImage(":/images/awesomeface.png").mirrored()); //mirrored消除镜像m_shaderProgram.setUniformValue("textureSmile", 1); //把纹理单元传给片段着色器中的采样器m_textureSmall = new QOpenGLTexture(QImage(":/images/small.png").mirrored()); //mirrored消除镜像m_shaderProgram.setUniformValue("textureSmall", 2); //把纹理单元传给片段着色器中的采样器m_shaderProgram.setUniformValue("mixValue", mixValue);
}void MyOpenGLWidget::resizeGL(int w, int h)
{Q_UNUSED(w);Q_UNUSED(h);//glViewport(0, 0, w, h);
}void MyOpenGLWidget::paintGL()
{//设置墨绿色背景glClearColor(0.2f, 0.3f, 0.3f, 1.0f); //状态设置glClear(GL_COLOR_BUFFER_BIT); //状态使用//变换矩阵QMatrix4x4 matrix; //创建单位矩阵unsigned int time = QTime::currentTime().msec();matrix.translate(0.5, -0.5, 0.0); //位移matrix.rotate(time, 0.0f, 0.0f, 1.0f); //旋转m_shaderProgram.setUniformValue("RotationMatrix", matrix);//绘制m_shaderProgram.bind(); //激活程序对象glBindVertexArray(VAO); //绑定VAOm_textureWall->bind(0); //绑定激活纹理单元0m_textureSmile->bind(1); //绑定激活纹理单元1m_textureSmall->bind(2); //绑定激活纹理单元2glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, NULL); //绘图
}void MyOpenGLWidget::keyPressEvent(QKeyEvent *event)
{if(event->key() == Qt::Key_Up)mixValue+=0.1;else if(event->key() == Qt::Key_Down)mixValue-=0.1;elsereturn;if(mixValue > 1.0)mixValue = 1.0;if(mixValue < 0.0)mixValue = 0.0;makeCurrent();m_shaderProgram.setUniformValue("mixValue", mixValue);doneCurrent();update();QOpenGLWidget::keyPressEvent(event);
}

定时器的槽函数中进行界面更新

void MyOpenGLWidget::onTimeout()
{update();
}

更改一下顶点数据中位置数据,使矩形变小(此处不是要实现的缩放功能,只是为展示效果更看)

//顶点数据
float vertices[] = {//位置              //颜色             //纹理0.5f,  0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, //右上角0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, //右下角-0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, //左下角-0.5f,  0.5f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f //左上角
};

在paintGL()函数中创建matrix变量,对matrix进行旋转和位移操作,之后把值传递给顶点着色器的RotationMatrix变量。

    //变换矩阵QMatrix4x4 matrix;unsigned int time = QTime::currentTime().msec();matrix.translate(0.5, -0.5, 0.0); //位移matrix.rotate(time, 0.0f, 0.0f, 1.0f); //旋转m_shaderProgram.setUniformValue("RotationMatrix", matrix);

在上面的代码中旋转代码在位移代码的下一行,变量matrix会先和旋转矩阵进行运算然后再和位移矩阵进行计算,达到先旋转后位移的效果。

运行结果如下,矩形会移动到界面的右下角,同时每隔100ms进行一次旋转

注:观看OpenGL中文官网(https://learnopengl-cn.github.io/)和阿西拜的现代OpenGL入门(https://ke.qq.com/course/3999604#term_id=104150693)学习OpenGL

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/775914.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数据结构——快速排序的三种方法和非递归实现快速排序

数据结构——快速排序的三种方法和非递归实现快速排序&#xff08;升序&#xff09; 快速排序的单趟排序hoare法挖坑法前后指针法 快速排序的实现key基准值的选取快速排序代码快速排序的优化 快速排序&#xff08;非递归&#xff09; 快速排序的单趟排序 hoare法 思路:从给定…

后端前行Vue之路(二):模版语法之插值与指令

1.概述 Vue.js的模板语法是一种将Vue实例的数据绑定到HTML文档的方法。Vue的模板语法是一种基于HTML的扩展&#xff0c;允许开发者将Vue实例中的数据绑定到HTML元素&#xff0c;以及在HTML中使用一些简单的逻辑和指令。Vue.js 基于 HTML 的模板语法允许开发者声明式地将 DOM 绑…

Windows11系统缺少解决办法

一.缺少msvcp120.dll 下载Mircrosoft Visual C 2015等系统关键组件 Microsoft Visual C 2015-2022 Redistributable (x86) - 14.34.31931 Installation Error etc.. - Microsoft Q&A 二.缺少python27.dll 重新下载python2.7进行安装(选择Windows x86-64 MSI installer)…

三级等保建设技术方案-Word

1信息系统详细设计方案 1.1安全建设需求分析 1.1.1网络结构安全 1.1.2边界安全风险与需求分析 1.1.3运维风险需求分析 1.1.4关键服务器管理风险分析 1.1.5关键服务器用户操作管理风险分析 1.1.6数据库敏感数据运维风险分析 1.1.7“人机”运维操作行为风险综合分析 1.2…

IP如何异地共享文件?

【天联】 组网由于操作简单、跨平台应用、无网络要求、独创的安全加速方案等原因&#xff0c;被几十万用户广泛应用&#xff0c;解决了各行业客户的远程连接需求。采用穿透技术&#xff0c;简单易用&#xff0c;不需要在硬件设备中端口映射即可实现远程访问。 异地共享文件 在…

腾讯云2核2G服务器CVM S5和轻量应用服务器优惠价格

腾讯云2核2G服务器多少钱一年&#xff1f;轻量服务器61元一年&#xff0c;CVM 2核2G S5服务器313.2元15个月&#xff0c;腾讯云2核2G服务器优惠活动 txyfwq.com/go/txy 链接打开如下图&#xff1a; 腾讯云2核2G服务器价格 轻量61元一年&#xff1a;轻量2核2G3M、3M带宽、200GB月…

AXI Memory Mapped to PCI Express 学习笔记(五)—— Test Bench

本文包含有关Vivado Design Suite环境中提供的测试平台&#xff08;Test Bench&#xff09;的信息。 一、Endpoint的Root Port模型测试平台 PCI Express Root Port Model是一个强大的测试平台环境&#xff0c;它提供了一个测试程序接口&#xff0c;可以与提供的PIO设计&#…

洛谷_P4995 跳跳!_python写法

P4995 跳跳&#xff01; - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) n int(input()) data list(map(int,input().split())) data.append(0) data.sort()sum 0 l 0 r len(data)-1 flag 1 while l<r:sum (data[l]-data[r])**2if flag:l 1flag 0else:r - 1flag 1…

LinkedList讲解指南

咦咦咦&#xff0c;各位小可爱&#xff0c;我是你们的好伙伴——bug菌&#xff0c;今天又来给大家普及Java SE相关知识点了&#xff0c;别躲起来啊&#xff0c;听我讲干货还不快点赞&#xff0c;赞多了我就有动力讲得更嗨啦&#xff01;所以呀&#xff0c;养成先点赞后阅读的好…

教育数字化调研团走进锐捷,共议职业教育数字化转型新思路

为贯彻落实国家教育数字化战略行动部署和2024年全国教育工作会议精神,加快推进职业教育数字化转型与发展,梳理职业教育数字化转型的现状、问题及发展趋势,并总结展示职业教育数字化转型的好经验、好做法,培育职业教育数字化创新成果,推动数字技术与职业教育深度融合、提高数字化…

ensp的PPP实验报告

实验要求&#xff1a; 1、R1和R2使用PPP链路直连&#xff0c;R2和R3把2条PPP链路捆绑为PPP MP直连 2、按照图示配置IP地址 3、R2对R1的PPP进行单向chap验证 4、R2和R3的PPP进行双向chap验证 1、配置ip地址 R1&#xff1a; [R1] int Serial 3/0/0 [Rl-Seria13/0/0] ip add 192…

机器学习——聚类算法-DBSCAN

机器学习——聚类算法-DBSCAN DBSCAN&#xff08;Density-Based Spatial Clustering of Applications with Noise&#xff09;是一种基于密度的聚类算法&#xff0c;可以发现任意形状的簇&#xff0c;并能有效处理噪声数据。本文将介绍DBSCAN算法的核心概念、算法流程、优缺点…

Kali远程操纵win7

一.准备 1.介绍 攻击方&#xff1a;kali IPV4:192.168.92.133 被攻击方&#xff1a;win7 IPV4:192.168.92.130 2.使用永恒之蓝漏洞 (1.使用root权限 (2.进入msfconsole (3.添加rhosts (4.run进行一下 二.进行远程操作 1.获取用户名和密码 在cmd5查询 2.获取syste…

抓包工具charles修改请求和返回数据

数据篡改的主要使用场景&#xff1a; &#xff08;1&#xff09;mock场景&#xff0c;mock入参和返回值参数&#xff0c;实现mock测试 &#xff08;2&#xff09;安全测试&#xff0c;对于支付金额等比较重要的字段&#xff0c;可以修改请求参数来进行安全测试 1.首先选择要…

Qt中QIcon图标设置(标题、菜单栏、工具栏、状态栏图标)

1 exe程序图标概述 在 Windows 操作系统中&#xff0c;程序图标一般会涉及三个地方&#xff1b; &#xff08;1&#xff09; 可执行程序&#xff08;以及对应的快捷方式&#xff09;的图标 &#xff08;2&#xff09; 程序界面标题栏图标 &#xff08;3&#xff09;程序在任务…

[激光原理与应用-77]:基于激光器加工板卡的二次开发软件的系统软硬件架构

目录 一、1个板卡、1个激光器、1个振镜的应用架构、1个工位 &#xff08;1&#xff09;PLC &#xff08;2&#xff09;MES &#xff08;3&#xff09;加工板卡 &#xff08;4&#xff09;激光加工板卡与激光器之间的转接卡 &#xff08;5&#xff09;DB25、DB15 &#x…

Typecho如何去掉/隐藏index.php

Typecho后台设置永久链接后&#xff0c;会在域名后加上index.php&#xff0c;很多人都接受不了。例如如下网址&#xff1a;https://www.jichun29.cn/index.php/archives/37/&#xff0c;但我们希望最终的形式是这样&#xff1a;https://www.jichun29.cn/archives/37.html。那么…

图神经网络实战(6)——使用PyTorch构建图神经网络

图神经网络实战&#xff08;6&#xff09;——使用PyTorch构建图神经网络 0. 前言1. 传统机器学习与人工智能2. 人工神经网络基础2.1 人工神经网络组成2.2 神经网络的训练 3. 图神经网络4. 使用香草神经网络执行节点分类4.1 数据集构建4.2 模型构建4.3 模型训练 5. 实现香草图神…

大话设计模式之装饰模式

装饰模式&#xff08;Decorator Pattern&#xff09;是一种结构型设计模式&#xff0c;它允许向现有对象动态地添加新功能&#xff0c;同时又不改变其结构。装饰模式通过将对象放入包装器中来实现&#xff0c;在包装器中可以动态地添加功能。 在装饰模式中&#xff0c;通常会有…

【教程】JavaScript代码混淆及优化

摘要 本文将介绍常见的JavaScript代码混淆技术&#xff0c;包括字符串转十六进制、Unicode编码、Base64加密、数值加密、数组混淆、花指令、逗号表达式、控制流程平坦化和eval执行。通过对这些混淆技术的理解和应用&#xff0c;可以提高代码的安全性和保护知识产权。 引言 随…