计算向量之间的旋转


前几天在咪咕工作时碰到了这样一个问题: 绘制圆柱体时圆柱体默认朝向z轴正向(屏幕外), 现在需要将其转至任意方向, 以此绘制任意方向上的圆柱体, 本来以为这个问题很简单, 但居然也困了我两个小时, 作为数学系的学生来说真是羞耻QAQ

首先需要计算旋转轴, 直接将当前方向(即(0,0,1)) 与目标方向$d$叉乘得到的向量再进行归一化(应该也可以不进行归一化) 作为旋转轴即可.

至于旋转角度, 直接通过计算当前方向(即(0,0,1)) 与目标方向$d$的内积并求其反余弦值即可得到, 核心代码如下所示:

glPushMatrix();
glm::vec3 v1(0, 0, 1);
glm::vec3 v2(p1->x - p0->x, p1->y - p0->y, p1->z - p0->z);
float dotVal = glm::dot(v1, v2);
float rotateTheta = glm::acos(glm::dot(v1, v2) / glm::length(v2));

glTranslatef(p0->x, p0->y, p0->z);
glm::vec3 rotateAxis = glm::normalize(glm::cross(v1, v2));
glRotatef(rotateTheta*180.0f / PI, rotateAxis.x, rotateAxis.y, rotateAxis.z);

//glm::mat3 rotateMatrix = glm::mat3(1.0);
//if (!(fabs(rotateTheta) < 1e-7 || fabs(rotateTheta - PI) < 1e-7)) {
//	glm::vec3 rotateAxis = glm::normalize(glm::cross(v1, v2));
//	glRotatef(rotateTheta*180.0f / PI, rotateAxis.x, rotateAxis.y, rotateAxis.z);
//	glm::mat3 rotateMatrix = glm::rotate(glm::mat4(1.0f), -rotateTheta, rotateAxis);
//}
//glm::vec3 rotate_p0 = rotateMatrix*glm::vec3(p0->x, p0->y, p0->z);
//glTranslatef(rotate_p0.x, rotate_p0.y, rotate_p0.z);
//glm::vec3 rotate_p01 = rotateMatrix*v2;

if (cm == 2) gluSphere(cylinder, CYLINDER_RADIUS, CYLINDER_SLICES, CYLINDER_SLICES);
float cylinderHeight = glm::length(glm::vec3(p1->x - p0->x, p1->y - p0->y, p1->z - p0->z));
gluCylinder(cylinder, CYLINDER_RADIUS, CYLINDER_RADIUS, cylinderHeight, CYLINDER_SLICES, CYLINDER_SLICES);
glPopMatrix();
glPushMatrix();
glTranslatef(p1->x, p1->y, p1->z);
if (cm == 2) gluSphere(cylinder, CYLINDER_RADIUS, CYLINDER_SLICES, CYLINDER_SLICES);
glPopMatrix();

主要是用glm实现的, 有两点需要注意下:
1. 通过glm::acos函数得到的度数是遵循弧度制而非角度制, 所以在用glRotatef函数进行旋转时还需要将其转为角度制.
2. glPushMatrix()和glPopMatrix()是两个好东西, 可以消除上一次的变换对本次变换的影响, 使本次变换是以世界坐标系的原点为参考点进行. 因为使用glRotatef函数进行旋转时其坐标系也相应地进行了旋转, 如果不使用glPushMatrix()和glPopMatrix()的话就需要计算圆柱体的方向向量在新的坐标系下的坐标, 为了方便便再次转回世界坐标系了. (其实就是对这坐标系的转换有点头晕. 羞耻QAQ)

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注