Computer Graphics Experiment II Part.1 3D Touring

图形学实验二,主要是写了3D的空间漫游以及Walking Boxman两个东西,用了一下午加一晚上。 先放一下效果:(我大B站时不时傲娇,加载不出来播放器的话烦请移步av9570127) 然后具体的实现主要分为两部分,3D场景漫游和机器人的绘画与动作,本篇博文先着重介绍3D漫游部分,机器人部分留作下一篇博文再做介绍。

UNV相机及变换

首先UNV相机主要由三个单位向量表示:

  • $\overrightarrow { n } $:指向目标的视线向量
  • $\overrightarrow { u } $:指向相机右方的向量
  • $\overrightarrow { v } $:指向相机上方的向量

而这三个组成的向量可以由下面三个向量(点)来求得:

  • $\overrightarrow { pos } $:相机位置
  • $\overrightarrow { target } $:视线目标位置
  • $\overrightarrow { up } $:指向空间上方的向量

具体的计算公式为:

有了这几个向量,我们就可以首先实现平移变换了:

1
2
3
4
5
6
7
8
9
void GLCamera::slide(float du, float dv, float dn,float dup)
{
m_pos(0) = m_pos(0) + du*u.x() + dv*v.x() + dn*n.x() + dup*m_up.x();
m_pos(1) = m_pos(1) + du*u.y() + dv*v.y() + dn*n.y() + dup*m_up.y();
m_pos(2) = m_pos(2) + du*u.z() + dv*v.z() + dn*n.z() + dup*m_up.z();
m_target(0) = m_target(0) + du*u.x() + dv*v.x() + dn*n.x() + dup*m_up.x();
m_target(1) = m_target(1) + du*u.y() + dv*v.y() + dn*n.y() + dup*m_up.y();
m_target(2) = m_target(2) + du*u.z() + dv*v.z() + dn*n.z() + dup*m_up.z();
}

其中,du dv dn dup分别是在$\overrightarrow { u } $, $\overrightarrow { v } $, $\overrightarrow { n } $, $\overrightarrow { up } $向量方向上面的平移距离。 然后将键盘的按键事件与函数绑定,在display函数中调用glulookAt函数就好了:

1
2
3
gluLookAt(cam.m_pos.x(), cam.m_pos.y(), cam.m_pos.z(), 
cam.m_target.x(), cam.m_target.y(), cam.m_target.z(),
cam.m_up.x(), cam.m_up.y(), cam.m_up.z())

鼠标的移动与视野变换

鼠标的移动产生视野的变换似乎是一个比较麻烦的事情,但是实际上这种视野的变换无非是一系列对unv三个向量的旋转操作,下面从两个维度说明下鼠标的移动对视野产生的影响应该怎么计算:

x方向变化——鼠标左右移动

如图,结合我们多年守望先锋的经验,左右移动鼠标是$\overrightarrow { u } $ $\overrightarrow { n } $ 两个向量绕着$\overrightarrow { v } $ 进行旋转,实际上,我们只需要将$\overrightarrow { n } $进行旋转后计算出旋转后对应的$\overrightarrow { target } $向量,并重新应用之前的计算公式计算$\overrightarrow { u } $ $\overrightarrow { n } $ $\overrightarrow { v } $。

y方向变化——鼠标上下移动

同样的,结合我们多年76对空打法鸡的经验,上下移动鼠标是$\overrightarrow { v } $ $\overrightarrow { n } $ 两个向量绕着$\overrightarrow { u } $ 进行旋转,实际上,我们也只需要将$\overrightarrow { n } $进行旋转后计算出旋转后对应的$\overrightarrow { target } $向量,并重新应用之前的计算公式计算$\overrightarrow { u } $ $\overrightarrow { n } $ $\overrightarrow { v } $。 注意下代码中的$x$ $y$和上面所说的$x$ $y$是反的,由于默认的二维数组是$x$为行,$y$为列……

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
void passiveMotion(int x, int y)
{
GLint deltax = oldx - x;
GLint deltay = oldy - y;
GLfloat xAngle = 360 * (GLfloat)deltay / (GLfloat)WinH;
GLfloat yAngle = 360 * (GLfloat)deltax / (GLfloat)WinW;
yAngle = 360 - yAngle;
xAngle = 360 - xAngle;
glPushMatrix();
glLoadIdentity();
glRotated(xAngle, cam.u.x(), cam.u.y(), cam.u.z());//上下移动
glRotated(yAngle, cam.v.x(), cam.v.y(), cam.v.z());//左右移动
GLfloat m[16];
glGetFloatv(GL_MODELVIEW_MATRIX, m);
glPopMatrix();
Eigen::Matrix3d rot;
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
rot(i, j) = m[i * 4 + j];
}
}
cam.n = rot*cam.n;
cam.m_target = Eigen::Vector3d(cam.m_pos.x() - cam.n.x(), cam.m_pos.y() - cam.n.y(), cam.m_pos.z() - cam.n.z());
cam.upduvn();
oldx = x;
oldy = y;
glutPostRedisplay();
}

到这里这个项目的目前实现了的3D漫游功能和实现的过程就差不多介绍完了,还有很多的相机旋转的函数并没有在这里用到,更具体的代码请移步github 下一篇博文——3D Boxman