GLM:旋转网格时如何在鼠标下保持相同的点?

时间:2014-10-31 13:52:25

标签: opengl graphics 3d mesh glm-math

我试图通过点击来旋转网格(但传统的arcball algorithm)。我们的想法是,当您单击并拖动鼠标时,您单击的网格上的点将完全保留在鼠标下(网格将围绕其原点旋转)。不幸的是,就我现在这样做的方式,点击的点在鼠标翻译时不会留在鼠标下。

为了投射光线并查看它是否与网格相交,我按以下方式计算鼠标世界坐标:

                           //mousePos is normalized -1 .. 1
 glm::vec4 mouse_clip = vec4(mousePos.x,mousePos.y,0,1);
 glm::vec4 mouse_world = glm::inverse(viewMatrix) * glm::inverse(projectionMatrix) * mouse_clip;

使用它来测试来自鼠标的光线是否与网格相交的代码可以正常工作。旋转时问题就开始了。这是一个解释旋转算法的图表。在下图中,您可以想象从顶部看3d场景:

rotation algorithm

我存储第一次点击与网格的交点的世界坐标,然后通过在鼠标移动时平移该点来获得第二个点。从茶壶原点到第一次点击的矢量是v1,第二点是v2。

通过交叉乘积v1 x v2我得到旋转轴。 通过做v1点v2,我得到它们之间角度的余弦。我使用glm :: acos来找到角度(以弧度表示?)

以下是鼠标移动后立即计算旋转的代码:

//this is a function that receives a vec3 translation. 
//Translation is a vector that describes the translation of the mouse
//in world coordinates (as calculated earlier).

//pick up teapot position from the model matrix
//it will serve as origin of the 2 vectors
vec3 origin = vec3(modelMatrix[3]);

//translate the point the mesh was hit previously
//by the same amount the mouse moved (normally no Z translation)
vec3 newPoint = previousPoint + translation;

vec3 v1 = glm::normalize(previousPoint - origin);  //unit vector from teapot center to first point
vec3 v2 = glm::normalize(newPoint - origin);   //unit vector from teapot center to translated point

vec3 axis = glm::normalize(glm::cross(v1,v2));  //find rotation axis
float angle = glm::acos(glm::dot(v1,v2));       //find rotation angle

modelMatrix = glm::rotate(modelMatrix,angle,axis); //rotate the modelMatrix accordingly

//store point for next mouse movement
previousPoint = newPoint;

然而,当我移动鼠标时,发生的旋转小于应该发生的旋转。点击的点不会停留在鼠标下方。以下是该问题的视频:

rotation video

如何让点击的点留在鼠标下?

1 个答案:

答案 0 :(得分:1)

您不应该将光线投射用于第二点(V2),而是将其设置为距离V1中心相同的距离,同时仍然位于鼠标下方。

从概念上讲,你可以将arcball radius = dist(center, V1)和raycast V2制作成。 (射线球交叉解决方案可用)。

除此之外,我发现您可以glm::normalize(glm::fquat(1+glm::dot(v1,v2), glm::cross(v1,v2)));进行轮换而不会弄乱acos和轴角(无论如何都会立即执行cos)。