我试图通过点击来旋转网格(但不传统的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场景:
我存储第一次点击与网格的交点的世界坐标,然后通过在鼠标移动时平移该点来获得第二个点。从茶壶原点到第一次点击的矢量是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;
然而,当我移动鼠标时,发生的旋转小于应该发生的旋转。点击的点不会停留在鼠标下方。以下是该问题的视频:
如何让点击的点留在鼠标下?
答案 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
)。