图形学实验二,主要是写了3D的空间漫游以及Walking Boxman两个东西,用了一下午加一晚上。 先放一下效果:(我大B站时不时傲娇,加载不出来播放器的话烦请移步av9570127) 然后具体的实现主要分为两部分,3D场景漫游和机器人的绘画与动作,本篇博文先着重介绍3D漫游部分,机器人部分留作下一篇博文再做介绍。 UNV相机及变换 --------
首先UNV相机主要由三个单位向量表示:
- \(\overrightarrow { n }\):指向目标的视线向量
- \(\overrightarrow { u }\):指向相机右方的向量
- \(\overrightarrow { v }\):指向相机上方的向量
而这三个组成的向量可以由下面三个向量(点)来求得:
- \(\overrightarrow { pos }\):相机位置
- \(\overrightarrow { target }\):视线目标位置
- \(\overrightarrow { up }\):指向空间上方的向量
具体的计算公式为: \[\begin{cases}
\overrightarrow { n } =\overrightarrow { target } -\overrightarrow { pos
} \\ \overrightarrow { u } =\overrightarrow { up } \times
\overrightarrow { n } \\ \overrightarrow { v } =\overrightarrow { u }
\times \overrightarrow { n } \end{cases}\]
有了这几个向量,我们就可以首先实现平移变换了: 1
2
3
4
5
6
7
8
9void 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();
}1
2
3gluLookAt(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
28void 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();
}