一、
MFC
框架下使用
OpenGL
绘图演示平移、旋转、缩放等操作的具体实现
二、
实现算法
绘制的图形以各种顶点及顶点之间的线、面构成
要想对这些图形进行操作,只需要对各种顶点在坐标系中的坐标进行转换即可
顶点在
OpenGL
中的坐标以四维形式表示:(
X,Y,Z,W
)
其中
X,Y,Z
对应三维系统中
X
,
Y
,
Z
三个柚;
W
默认为
1
,一般为了方便各种操作时坐标换算而将三维坐标系提升为四次,即齐次坐标系。
①
平移操作——对应
glTranslate
函数:
初始点:(
X,Y,Z,W
)
使用转换矩阵
TMatrix
:
1, 0, 0, Tx
0, 1, 0, Ty
0, 0, 1, Tz
0, 0, 0, 1
(X’, Y’, Z’) =
TMatrix * (X, Y, Z),
其中
Tx
,
Ty
,
Tz
对应
glTranslate
函数中的三个参数,分别表示三个方向的的平移量;原始坐标右乘平移矩阵后即得到新的坐标
③
缩放操作——对应
glScale
函数:
使用转换矩阵为
SMatrix
:
Sx, 0, 0, 0
0, Sy, 0, 0
0, 0, Sz, 0
0, 0, 0, 1
(X’, Y’, Z’) =
SMatrix * (X, Y, Z)
此次自己实现的缩放函数与
OpenGL
有所不同,我实现的是与图形的中心点为基准进行缩放,所以进行矩阵变换前需要计算各个坐标的位置并求几何中心。
②
旋转操作——对应
glRotate
函数(逆时针方向):
在三维坐标系统中,旋转操作与二维情况有所不同,因为三维中的一个点可以绕不同的方向进行旋转,所以除了要指定旋转角度外,还要指定旋转轴
当所取旋转轴为
X
,
Y
或
Z
三个坐标轴时,与二维情况相似
即所选取的轴那一方向上的坐标不变,在其它两个轴所成的平面上进行二维旋转变换
根据这个规律可以把三维中绕任意轴旋转的操作分为几个操作来完成
步骤:
先将待旋转的坐标与旋转轴平移,使旋转轴经过原点
进行若干次旋转操作,例旋转轴与
Z
轴正向重合
依据对坐标轴旋转的方法将待旋转坐标进行旋转操作
应用逆旋转使旋转轴回到原方向
应用逆平移使旋转轴回到原位置
二维情况下旋转矩阵为:
cosQ, -sinQ, 0
sinQ, cosQ, 0
0, 0, 1
此次采用先绕
X
转做旋转
Rx
,再绕
Y
转旋转
Ry
使旋转轴与
Z
转正向重合,再绕
Z
轴做
Rz
旋转
Q
度,再进行逆操作还原位置
即
: (X’, Y’, Z’) = Rx*Ry*Rz(Q)*(Ry
-1
)*(Rx-1)
其中
Q
是需要旋转的角度,
逆矩阵
A
-1
= A*/|A|
代数余子式:
(-1)^(i+j)Aij
三、
程序结构
使用
MFC
生成程序界面,
OpenGL
负责绘制图形
文档
/
视图结构
文件数据为,图形的类型以及各个顶点坐标
从菜单中选择要进行的操作:平移,旋转,缩放后出现对话框输入参数
平移——
3
个参数,分别表示
X,Y,Z
各个轴向的平移量
旋转——
4
个参数,第一个角度
Angle,
后三个表示转轴向量
缩放——
3
个参数,表示
X,Y,Z
轴方向上的缩放比例,可以为负数
文件数据为:
一个
CArray<Point,
Point> m_PointArray
保存图形的各个顶点信息
一个表示图形形状的
int m_nShape
表示
glBegin
函数里的参数
四、
MFC
中使用
OpenGL
绘图
具体还没有研究,只是照搬网上教程,以后补上
View
头文件中加入
HGLRC m_hRC; //Rendering
Context
CDC* m_pDC; //Device
Context
BOOL InitializeOpenGL(); //Initialize OpenGL
BOOL SetupPixelFormat(); //Set up the Pixel Format
实现部分:
InitializeOpenGL
BOOL CHomeWork1View::InitializeOpenGL()
{
//
Get a DC for the Client Area
m_pDC
=
new
CClientDC(
this
);
//
Failure to Get DC
if
(m_pDC
==
NULL)
{
MessageBox(_T(
"
Error Obtaining DC
"
));
return
FALSE;
}
//
Failure to set the pixel format
if
(
!
SetupPixelFormat())
{
return
FALSE;
}
//
Create Rendering Context
m_hRC
=
::wglCreateContext (m_pDC
->
GetSafeHdc ());
//
Failure to Create Rendering Context
if
(m_hRC
==
0
)
{
MessageBox(_T(
"
Error Creating RC
"
));
return
FALSE;
}
//
Make the RC Current
if
(::wglMakeCurrent (m_pDC
->
GetSafeHdc (), m_hRC)
==
FALSE)
{
MessageBox(_T(
"
Error making RC Current
"
));
return
FALSE;
}
//
Specify Black as the clear color
::glClearColor(
0.0f
,
0.0f
,
0.0f
,
0.0f
);
//
Specify the back of the buffer as clear depth
::glClearDepth(
1.0f
);
//
Enable Depth Testing
::glEnable(GL_DEPTH_TEST);
return
TRUE;
}
SetupPixelFormat
BOOL CHomeWork1View::SetupPixelFormat()
{
static
PIXELFORMATDESCRIPTOR pfd
=
{
sizeof
(PIXELFORMATDESCRIPTOR),
//
size of this pfd
1
,
//
version number
PFD_DRAW_TO_WINDOW
|
//
support window
PFD_SUPPORT_OPENGL
|
//
support OpenGL
PFD_DOUBLEBUFFER,
//
double buffered
PFD_TYPE_RGBA,
//
RGBA type
24
,
//
24-bit color depth
0
,
0
,
0
,
0
,
0
,
0
,
//
color bits ignored
0
,
//
no alpha buffer
0
,
//
shift bit ignored
0
,
//
no accumulation buffer
0
,
0
,
0
,
0
,
//
accum bits ignored
16
,
//
16-bit z-buffer
0
,
//
no stencil buffer
0
,
//
no auxiliary buffer
PFD_MAIN_PLANE,
//
main layer
0
,
//
reserved
0
,
0
,
0
//
layer masks ignored
};
int
m_nPixelFormat
=
::ChoosePixelFormat(m_pDC
->
GetSafeHdc(),
&
pfd);
if
( m_nPixelFormat
==
0
)
{
return
FALSE;
}
if
( ::SetPixelFormat(m_pDC
->
GetSafeHdc(), m_nPixelFormat,
&
pfd)
==
FALSE)
{
return
FALSE;
}
return
TRUE;
}
此外需要将
View
类中的
OnEraseBkgnd
消息屏蔽掉,使之直接返回
TRUE
,因为消除背景的工作已经将由
OpenGL
来做,不需要由这个函数再来做一遍。
在
PreCreateWindow
函数中修改
cs.style |= WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
保证
OpenGL
不会绘制到其它窗口中去
在
OnCreate
函数中调用
InitializeOpenGL
函数就可以在
View
中进行绘制了
另外,为了适应窗口大小变化,还需要在
OnSize
函数中加入高速视域的代码
OnSize
void
CHomeWork1View::OnSize(UINT nType,
int
cx,
int
cy)
{
CView::OnSize(nType, cx, cy);
//
TODO: 在此处添加消息处理程序代码
GLdouble aspect_ratio;
//
width/height ratio
if
(
0
>=
cx
||
0
>=
cy )
{
return
;
}
//
select the full client area
::glViewport(
0
,
0
, cx, cy);
//
compute the aspect ratio
//
this will keep all dimension scales equal
aspect_ratio
=
(GLdouble)cx
/
(GLdouble)cy;
//
select the projection matrix and clear it
::glMatrixMode(GL_PROJECTION);
::glLoadIdentity();
//
select the viewing volume
::gluPerspective(
120
, aspect_ratio,
1.0f
,
150.0f
);
//
switch back to the modelview matrix and clear it
::glMatrixMode(GL_MODELVIEW);
::glLoadIdentity();
}
做好一切准备工作后,就可以在
OnDraw
中使用
OpenGL
函数进行绘制了,不过别忘了程序最后要进行资源的释放,在
OnDestroy
中加入清理工作
OnDestroy
void
CHomeWork1View::OnDestroy()
{
CView::OnDestroy();
//
TODO: 在此处添加消息处理程序代码
//
Make the RC non-current
if
(::wglMakeCurrent (
0
,
0
)
==
FALSE)
{
MessageBox(_T(
"
Could not make RC non-current
"
));
}
//
Delete the rendering context
if
(::wglDeleteContext (m_hRC)
==
FALSE)
{
MessageBox(_T(
"
Could not delete RC
"
));
}
//
Delete the DC
if
(m_pDC)
{
delete m_pDC;
}
//
Set it to NULL
m_pDC
=
NULL;
}
最后程序演示结果:
五、
编程中遇到的问题总结
1.
复习了操作符重载的相关内容
2.
浮点数的比较,这个比较重要,以前在课上只是听说,没有特别印象,在耗费了我
2
个小时的调试之后,以后应该再也不会忘了。
Fabs
(
f1-f2
)
<=
精度要求
3.
此次写的工具类
Matrix
胜于处理向量,矩阵的相关操作,因为偷懒还不太完善,尤其是矩阵的一些操作比较求模,求逆都没有实现,以后补上
http://www.cppblog.com/Files/sunshinealike/HomeWork1.rar