文章目录
在三维图形学中,几何变换大致分为三种,平移变换(Translation),缩放变换(Scaling),旋转变换(Rotation)。
1、 平移变换(Translation)
1、二维空间
在二维空间中一个点[x,y],移动到另一个点[x’,y’],假设两个点分别沿着坐标轴x,y移动了Tx与Ty,即:
x’ = x + Tx
y’ = y + Ty
为了更好地描述和表达这种平移变换,我们将线性方程转换为矩阵的形式;
[
x
′
y
′
]
=
[
1
0
T
x
0
1
T
y
]
∗
[
x
y
]
\begin{bmatrix} x' \\y' \end{bmatrix}=\begin{bmatrix} 1& 0 &Tx \\ 0& 1 &Ty \end{bmatrix}*\begin{bmatrix} x\\ y \end{bmatrix}
[x′y′]=[1001TxTy]∗[xy]
但是这样是无法直接运算的,为了解决这个问题,我们引入了齐次坐标,所以将上式转换为:
#include <graphics.h> // 引用图形库头文件
#include <conio.h>
#include <Eigen/core>
using namespace Eigen;
void transform(int x1, int y1, int x2, int y2,double Tx,double Ty)
{
line(x1, y1, x2, y2);
Eigen::Matrix3d tranMatrix;
tranMatrix << 1, 0, Tx, 0, 1, Ty, 0, 0, 01;
Eigen::Vector3d vecbegin, vecend,vecb,vece;
vecbegin << x1, y1, 1;
vecend << x2, y2, 1;
vecb = Eigen::Vector3d::Zero();
vece = Eigen::Vector3d::Zero();
vecb = tranMatrix * vecbegin;
vece = tranMatrix * vecend;
line(vecb[0], vecb[1], vece[0], vece[1]);
}
int main()
{
//沿x平移100,沿y平移50;
initgraph(640, 480); // 创建绘图窗口,大小为 640x480 像素
transform(200, 200, 300, 300,100,50);
_getch(); // 按任意键继续
closegraph(); // 关闭绘图窗口
return 0;
}
此时需要使用Eigen库,Eigen库在windows上配置与Linux一样,只需要指定头文件就可以。
[
x
′
y
′
1
]
=
[
1
0
T
x
0
1
T
y
0
0
1
]
∗
[
x
y
1
]
\begin{bmatrix} x' \\y' \\ 1 \end{bmatrix}=\begin{bmatrix} 1& 0 &Tx \\ 0& 1 &Ty \\ 0 &0&1 \end{bmatrix}*\begin{bmatrix} x\\ y\\ 1 \end{bmatrix}
⎣⎡x′y′1⎦⎤=⎣⎡100010TxTy1⎦⎤∗⎣⎡xy1⎦⎤
2、三维空间
将三维空间中的一个点[x, y, z, 1]移动到另外一个点[x’, y’, z’, 1],三个坐标轴的移动分量分别为dx=Tx, dy=Ty, dz=Tz, 即
x’ = x + Tx
y’ = y + Ty
z’ = z + Tz
平移变换的矩阵如下。
[
x
′
y
′
z
′
1
]
=
[
1
0
0
T
x
0
1
0
T
y
0
0
1
T
z
0
0
0
1
]
∗
[
x
y
z
1
]
\begin{bmatrix} x'\\y'\\z'\\1 \end{bmatrix}=\begin{bmatrix} 1& 0 &0&Tx \\ 0& 1 &0&Ty \\ 0&0&1&Tz\\ 0 &0&0&1 \end{bmatrix}*\begin{bmatrix} x\\y\\z\\1 \end{bmatrix}
⎣⎢⎢⎡x′y′z′1⎦⎥⎥⎤=⎣⎢⎢⎡100001000010TxTyTz1⎦⎥⎥⎤∗⎣⎢⎢⎡xyz1⎦⎥⎥⎤
2、 缩放变换(Scaling)
1、二维空间
将模型放大或者缩小,本质也是对模型上每个顶点进行放大和缩小(顶点坐标值变大或变小),假设变换前的点是[x, y],变换后的点是[x’,y’]
x’ = x * Sx
y’ = y * Sy
同样我们需要将其转换为齐次坐标的形式,将线性表达式转换为矩阵:
[
x
′
y
′
1
]
=
[
S
x
0
0
0
S
y
0
0
0
1
]
∗
[
x
y
1
]
\begin{bmatrix} x'\\y'\\1 \end{bmatrix}=\begin{bmatrix} Sx& 0 & 0\\ 0& Sy & 0 \\ 0 & 0 &1 \end{bmatrix} *\begin{bmatrix} x\\y \\1 \end{bmatrix}
⎣⎡x′y′1⎦⎤=⎣⎡Sx000Sy0001⎦⎤∗⎣⎡xy1⎦⎤
#include <graphics.h> // 引用图形库头文件
#include <conio.h>
#include <Eigen/core>
using namespace Eigen;
void ScaleTransform(int x1, int y1, int x2, int y2, double scalex, double scaley)
{
line(x1, y1, x2, y2);
Eigen::Matrix3d tranMatrix;
tranMatrix << scalex, 0, 0, 0, scaley, 0, 0, 0, 1;
Eigen::Vector3d vecbegin, vecend, vecb, vece;
vecbegin << x1, y1, 1;
vecend << x2, y2, 1;
vecb = Eigen::Vector3d::Zero();
vece = Eigen::Vector3d::Zero();
vecb = tranMatrix * vecbegin;
vece = tranMatrix * vecend;
line(vecb[0], vecb[1], vece[0], vece[1]);
}
int main()
{
initgraph(640, 480); // 创建绘图窗口,大小为 640x480 像素
ScaleTransform(200, 200, 300, 300, 2, 0.5); //横坐标放大二倍,纵坐标缩短一半
_getch(); // 按任意键继续
closegraph(); // 关闭绘图窗口
return 0;
}
2、三维空间
将模型放大或者缩小,本质也是对模型上每个顶点进行放大和缩小(顶点坐标值变大或变小),假设变换前的点是[x, y, z, 1],变换后的点是[x’, y’, z’, 1],那么
x’ = x * Sx
y’ = y * Sy
z’ = z * Sz
缩放变换的矩阵如下:
[
x
′
y
′
z
′
1
]
=
[
S
x
0
0
0
0
S
y
0
0
0
0
S
z
0
0
0
0
1
]
∗
[
x
y
z
1
]
\begin{bmatrix} x'\\y'\\z'\\1 \end{bmatrix}=\begin{bmatrix} Sx& 0 & 0&0\\ 0& Sy & 0 &0\\ 0& 0&Sz&0\\ 0 & 0&0 &1 \end{bmatrix} *\begin{bmatrix} x\\y \\z\\1 \end{bmatrix}
⎣⎢⎢⎡x′y′z′1⎦⎥⎥⎤=⎣⎢⎢⎡Sx0000Sy0000Sz00001⎦⎥⎥⎤∗⎣⎢⎢⎡xyz1⎦⎥⎥⎤
3、 旋转变换(Rotation)
旋转矩阵的旋转其实包含两种意思,一是在同一个坐标系下,向量的旋转;二是坐标系的旋转,使得同一向量在不同的坐标系下有不同的坐标。
首先推导公式之前我们假设的旋转角都为正,即逆时针方向角度,所谓逆时针指的就是大拇指指向坐标正向时,四指旋转的抓握方向
1、 向量旋转
首先讨论二维平面坐标下的旋转,然后引申至三维。
1.1 平面二维旋转
如下图,XY坐标系中,向量OP旋转β角度到了OP’的位置:
根据三角函数关系,可以列出向量OP与OP’的坐标表示形式:
对比上面个两个式子,将第2个式子展开:
用矩阵形式重新表示为:
[
x
′
y
′
]
=
[
c
o
s
β
−
s
i
n
β
s
i
n
β
c
o
s
β
]
∗
[
x
y
]
\begin{bmatrix} x' \\y' \end{bmatrix}=\begin{bmatrix} cos\beta & -sin\beta\\ sin\beta& cos\beta \end{bmatrix}* \begin{bmatrix} x \\y \end{bmatrix}
[x′y′]=[cosβsinβ−sinβcosβ]∗[xy]
这就是二维旋转的基本形式,中间的矩阵即二维旋转的旋转矩阵,坐标中的某一向量左乘该矩阵后,即得到这个向量旋转β角后的坐标。
void Rotation(int x1, int y1, int x2, int y2, double beta)
{
//其中 beta 必须采用 弧度:PI/6
line(x1, y1, x2, y2);
Eigen::Matrix2d tranMatrix;
tranMatrix << cos(beta), -sin(beta), sin(beta), cos(beta);
std::cout << tranMatrix << std::endl;
Eigen::Vector2d point1, point2, point1_, point2_;
point1 << x1, y1;
point2 << x2, y2;
point1_ = tranMatrix * point1;
point2_ = tranMatrix * point2;
setlinecolor(RGB(255,0,0));
line(point1_[0], point1_[1], point2_[0], point2_[1]);
}
如果向量p顺时针旋转,则旋转矩阵为:
[ x ′ y ′ ] = [ c o s β s i n β − s i n β c o s β ] ∗ [ x y ] \begin{bmatrix} x' \\y' \end{bmatrix}=\begin{bmatrix} cos\beta & sin\beta\\ -sin\beta & cos\beta \end{bmatrix}*\begin{bmatrix} x\\y \end{bmatrix} [x′y′]=[cosβ−sinβsinβcosβ]∗[xy]
1.2 三维旋转
三维旋转可借助二维旋转来理解,由于三维空间中可以任意轴旋转,为方便分析与使用,只考虑绕X、Y、Z轴的旋转。
-
绕Z轴
参照上面的图,添加一个Z轴,则上面的二维旋转实际上就是绕Z轴的三维旋转
照搬上面的推导公式,并添加Z坐标的变换关系(实际是没有变),然后改写成矩阵形式,红色方框即为绕Z轴的旋转矩阵。
-
绕Y轴
绕Y轴旋转同理,这里直接改变坐标轴的符号表示,注意坐标顺序要符合右手系,我这里用颜色区分了不同的轴。最终的矩阵形式要进一步改写成XYZ的顺序。红色方框即为绕Y轴的旋转矩阵。
-
绕X轴
参照绕Y轴的推导,可以得到绕X轴的结果。红色方框即为绕X轴的旋转矩阵。
1.3 助记
对于单位矩阵,绕哪个轴旋转,哪一列不用变,然后将二维旋转矩阵替换对应的4个位置,注意,绕Y的旋转矩阵看起来与另外两个不同,它的-sinβ是在左下!
2、 坐标系旋转
2.1平面二维旋转
如下图,xy坐标系中,有一向量OP,其坐标可表示为(x,y),该向量与X轴夹角为α。然后,坐标系绕原点逆时旋转了β角度,形成新的坐标系x’y’,此时OP在新的坐标系中的坐标表示为(x,y),根据几何关系,可以得到如下推导,最终得到绿色虚框的旋转矩阵。
2.2 三维旋转
-
绕X轴
-
绕Y轴
-
绕Z轴
#include <pangolin/pangolin.h>
#include <Eigen/Core>
#include <Eigen/Geometry>
#include <iostream>
using namespace std;
const double PI=3.1415926;
void initSystem()
{
glColor4f(1.0f, 0.0f, 0.0f, 1.0f);
pangolin::glDrawLine(0, 0, 0, 1, 0, 0);
// pangolin::GlFont *text_font = new pangolin::GlFont("/usr/share/fonts/truetype/lyx/cmmi10.ttf", 20.0);
// text_font->Text(">").Draw(1.0,0.0,0.0); //设置文字;
glColor4f(0.0f, 1.0f, 0.0f, 1.0f);
pangolin::glDrawLine(0, 0, 0, 0, 1, 0);
glColor4f(0.0f, 0.0f, 1.0f, 1.0f);
pangolin::glDrawLine(0, 0, 0, 0, 0, 1);
}
void tranform(GLfloat x1,GLfloat y1,GLfloat z1,GLfloat x2,GLfloat y2,GLfloat z2,GLfloat Tx,GLfloat Ty,GLfloat Tz)
{
Eigen::Matrix<GLfloat,4,4> tranMatrix;
tranMatrix<<1,0,0,Tx,0,1,0,Ty,0,0,1,Tz,0,0,0,1;
glColor4f(0.0f,1.0f,1.0f,1.0f);
pangolin::glDrawLine(x1,y1,z1,x2,y2,z2);
Eigen::Matrix<GLfloat,4,1> point1,point2,point1_,point2_;
point1<<x1,y1,z1,1;
point2<<x2,y2,z2,1;
point1_=tranMatrix*point1;
point2_=tranMatrix*point2;
glColor4f(1.0f,1.0f,0.0f,1.0f);
pangolin::glDrawLine(point1_[0],point1_[1],point1_[2],point2_[0],point2_[1],point2_[1]);
}
void scale(GLfloat x1,GLfloat y1,GLfloat z1,GLfloat x2,GLfloat y2,GLfloat z2,GLfloat Sx,GLfloat Sy,GLfloat Sz)
{
Eigen::Matrix<GLfloat,4,4> tranMatrix;
tranMatrix<<Sx,0,0,0,0,Sy,0,0,0,0,Sz,0,0,0,0,1;
glColor4f(0.0f,1.0f,1.0f,1.0f);
pangolin::glDrawLine(x1,y1,z1,x2,y2,z2);
Eigen::Matrix<GLfloat,4,1> point1,point2,point1_,point2_;
point1<<x1,y1,z1,1;
point2<<x2,y2,z2,1;
point1_=tranMatrix*point1;
point2_=tranMatrix*point2;
glColor4f(1.0f,1.0f,0.0f,1.0f);
pangolin::glDrawLine(point1_[0],point1_[1],point1_[2],point2_[0],point2_[1],point2_[1]);
}
void rotationZ(GLfloat x1,GLfloat y1,GLfloat z1,GLfloat x2,GLfloat y2,GLfloat z2,GLfloat Beta)
{
Eigen::Matrix<GLfloat,3,3> tranMatrix;
tranMatrix<<cos(Beta),-sin(Beta),0,sin(Beta),cos(Beta),0,0,0,1;
glColor4f(0.0f,1.0f,1.0f,1.0f);
pangolin::glDrawLine(x1,y1,z1,x2,y2,z2);
Eigen::Matrix<GLfloat,3,1> point1,point2,point1_,point2_;
point1<<x1,y1,z1;
point2<<x2,y2,z2;
point1_=tranMatrix*point1;
point2_=tranMatrix*point2;
glColor4f(1.0f,1.0f,0.0f,1.0f);
pangolin::glDrawLine(point1_[0],point1_[1],point1_[2],point2_[0],point2_[1],point2_[1]);
}
int main(int /*argc*/, char ** /*argv*/)
{
pangolin::CreateWindowAndBind("Main", 640, 480);
glEnable(GL_DEPTH_TEST);
// Define Projection and initial ModelView matrix
pangolin::OpenGlRenderState s_cam(
pangolin::ProjectionMatrix(640, 480, 420, 420, 320, 240, 0.2, 100),
pangolin::ModelViewLookAt(-2, 2, -2, 0, 0, 0, pangolin::AxisY));
// Create Interactive View in window
pangolin::Handler3D handler(s_cam);
pangolin::View &d_cam = pangolin::CreateDisplay()
.SetBounds(0.0, 1.0, 0.0, 1.0, -640.0f / 480.0f)
.SetHandler(&handler);
while (!pangolin::ShouldQuit())
{
// Clear screen and activate view to render into
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
d_cam.Activate(s_cam);
//pangolin::glDrawAxis(1); //调用GL坐标轴;
// Render OpenGL Cube
initSystem();
//tranform(0,0,0,1,1,1,0.5,0,0);
//scale(0, 0, 0, 1, 1, 1,2,2,2);
rotationZ(0, 0, 0, 1, 1, 1,PI/6);
//Swap frames and Process Events
pangolin::FinishFrame();
}
return 0;
}