OGRE 成功的作品,主要是 天龙八部 和 火炬之光。
以前看代码,老是看不太懂,最近补充了一下基础理论。 摄像机方向控制三个方法:四元数、姿态角(Euler 角)、摄像矩阵(ViewMatrix),不同 3D 引擎 大同小异。
摄像机默认是近大远小的椎体视角,小地图使用的是类似平行光的视角,需要:
cam->setProjectionType(Ogre::PT_ORTHOGRAPHIC);
setDirection
不支持以 Ogre::Vector3::UNIT_Y
为参数。
原因:比如 setDirection
为向纸面内看物体 A,此时 Ogre 的显示为 A,而不是倒立的 A,这是因为 Ogre::Vector3::UNIT_Y
是 Ogre 默认的 'UP' 视角。
所以 setDirection(Ogre::Vector3::UNIT_Y)
会出错,需要先设定向上的坐标轴:
cam->setFixedYawAxis(true, gre::Vector3::NEGATIVE_UNIT_Z);
cam->setDirection(0.0f, -1.0f, 0.0f);
改变方向也可以通过 Yaw Roll Pitch 来实现。
注意以此旋转依旧要设:cam->setFixedYawAxis(true, Ogre::Vector3::NEGATIVE_UNIT_Z);
// 2 的代码写的麻烦一点就是:
cam->setFixedYawAxis(true, Ogre::Vector3::NEGATIVE_UNIT_Z);
cam->setDirection(1.0f, 0.0f, 0.0f);
cam->yaw(Ogre::Degree(90));
// 改变了 UP 之后,当前坐标系也不清晰了,也可以这样写
cam->setFixedYawAxis(true, Ogre::Vector3::NEGATIVE_UNIT_Z);
cam->setDirection(1.0f, 0.0f, 0.0f);
cam->rotate(Ogre::Vector3(0.0f, 0.0f, 1.0f), Ogre::Degree(-90));
// 也可以利用四元数旋转
cam->setFixedYawAxis(true, Ogre::Vector3::NEGATIVE_UNIT_Z);
cam->setDirection(1.0f, 0.0f, 0.0f);
cam->rotate(Ogre::Quaternion(Ogre::Degree(-90), Ogre::Vector3(0.0f, 0.0f, 1.0f)));
矩阵的方法,不再需要设置 'UP' 的方向。
世界矩阵 => 摄像矩阵 (ViewMatrix) => 投影矩阵 (ProjectionMatrix)
旋转是在 ViewMatrix 中的处理,我需要的矩阵只是 Y 轴和 Z 轴做了对换,所以:
1 0 0 0
0 0 -1 0
0 1 0 -1000 // -1000 是摄像机的位置
0 0 0 1
Ogre::Matrix4 mx = Ogre::Matrix4::IDENTITY;
mx[1][1] = 0.0f;
mx[1][2] = -1.0f;
mx[2][1] = 1.0f;
mx[2][2] = 0.0f;
mx[2][3] = -1000.f;
cam->setCustomViewMatrix(true, mx);
四元数的方法,不再需要设置 'UP' 的方向。
从 3 中的最后一个方法可以知道,四元数就是绕一个向量,旋转一定的角度。
事实上我的变化,一直是在受 setFixedYawAxis
干扰,实际只是原始坐标轴 X 旋转了 90 度。
cam->setOrientation(Ogre::Quaternion(Ogre::Degree(-90), Ogre::Vector3(1.0f, 0.0f, 0.0f)));
注 1:四元数的 w, x, y, z 分别等于:
float w = cos(angle / 2);
float x = axis.x * sin(angle / 2);
float y = axis.y * sin(angle / 2);
float z = axis.z * sin(angle / 2);
注 2:现在还不清楚四元数用于旋转时具体的计算方法,也没必要知道。 不过要记住四元数的 w 是不能为 0 的,也就是说不能旋转 180。 可以用下面代码回避:
if (1.0f + src.dotProduct(dir) < 0.0001f) {
// 点乘 =-1.0,那么就是方向相反了
src->yaw(Ogre::Degree(180));
} else {
Ogre::Quaternion quat = src.getRotationTo(dir);
src.rotate(quat);
}