OpenGL中的Trackball旋转

7

我在OpenGL中实现轨迹球旋转时遇到了问题。当我使用轨迹球旋转将我的立方体向右沿着X轴旋转90度(在屏幕上从左向右拖动鼠标),然后尝试通过将鼠标从屏幕顶部向底部拖动来旋转它,我希望立方体沿着我的视角的y轴旋转。但是,在旋转后,它沿着自己的y轴旋转,从我的视角侧面旋转。

有人能告诉我可能做错了什么吗?


2
你能否发布一个最小的GLUT程序来演示问题? - genpfault
4个回答

5

看起来你没有使用四元数来表示旋转。如果你在谷歌上搜索“Arcball Graphics Gems”,你应该能够找到Ken Shoemake在Graphic Gems IV中的代码。

或者,如果你只想要代码,请点击这里


1
NeHethis guy也有一些ArcBall代码。 - genpfault

3

对于Daniel关于四元数和Arcball的建议,我表示赞同。在表示方向和旋转时,您应始终使用四元数。

另一个提示:Gavin Bell实现的Arcball扩展使得在旋转球体外单击鼠标更加直观。它分发在Glut库的示例中,这里这里

它类似于NeHe的实现,但是在球体内外之间的过渡是平滑的。Bell的方法在许多开源3D库中使用,包括:


1

这是一个非常古老的问题,但我恰好遇到了完全相同的问题,因为我正在尝试从learnopengl.com调整相机代码以适应Arcball模型...

重要的是,我不想转向使用四元数,因为任何使用四元数实现的操作也可以使用标准的线性代数来实现。

我找到的解决方案是将弧球坐标系转换为相机坐标系(就像弧球和相机一起旋转一样)。

我还使用Rodrigues公式旋转了相机的位置、前向和上向量,然后使用glm:LookAt返回最终的ViewMatrix(即传递到顶点着色器中的Matrix)。

目前,我有一个快速而不太规范的解决方案,如下所示:

// Processes input received from a mouse input system. 
void ProcessMouseMovement(glm::vec2 prevMouse, glm::vec2 curMouse)
{
    glm::vec3 p1 = screen_to_arcball( curMouse );
    glm::vec3 p2 = screen_to_arcball( prevMouse );
    
    // Rotate arcball to camera coordinates
    p1 = glm::vec3( glm::inverse(ViewMatrix) * glm::vec4(p1, 0.0));
    p2 = glm::vec3( glm::inverse(ViewMatrix) * glm::vec4(p2, 0.0));
    
    glm::vec3 axis = glm::cross(p1, p2);

    float angle = std::acos(glm::dot(p1,p2));

    if ( angle > 0.001f)
    {
        // Rotate
        Position = rotate_axis_angle(Position, axis, angle);
        Front = rotate_axis_angle(Front, axis, angle);
        Up = rotate_axis_angle(Up, axis, angle);

        ViewMatrix = glm::lookAt(Position, Position + Front, Up);
    }
}

// Get Arcball coordinates from screen
glm::vec3 screen_to_arcball(const glm::vec2 &p)
{
    const float dist = glm::dot(p, p);
    if (dist < 0.999f) 
    {
        // If we're on/in the sphere return the point on it
        return glm::vec3(p.x, p.y, std::sqrt(1.f - dist));
    } 
    else 
    {
        // otherwise we project the point onto the sphere
        const glm::vec2 proj = glm::normalize(p);
        return glm::vec3(proj.x, proj.y, 0.f);
    }
}

// Rotate vector given (angle,axis) using Rodrigues' formula
glm::vec3 rotate_axis_angle(glm:: vec3 &vec, glm:: vec3 axis, float angle)
{
    axis = glm::normalize(axis);
    glm::vec3 cross_axis = glm::cross(axis, vec);
    vec = vec * std::cos(angle) + axis * glm::dot(axis,vec) * (1.0f - std::cos(angle)) + cross_axis * std::sin(angle); 
    return vec;
}

顺便说一下,我从Ospray存储库的先前版本ArcballCamera.cpp中获取了screen_to_arcball函数。现在他们有一个基于四元数的实现,看起来类似于Twinklebear的arcball_camera.cpp

干得好,我会尝试你的代码,并将它们添加到learnopengl.com的相机类中,谢谢! - ollydbg23
顺便说一句:看起来 https://github.com/ospray/ospray 已经更改了文件结构,你的链接现在已经失效了。 - ollydbg23
我看到一些类似的实现,也有pan()函数,请参见此处:https://github.com/Twinklebear/arcball-cpp/blob/master/arcball_camera.cpp,另外请注意在您的代码中,输入鼠标坐标是规范化的坐标,这意味着相机从应用程序中以规范化设备坐标接收输入,其中屏幕的左上角为[-1,1],右下角为[1,-1],如上面的链接所述。 - ollydbg23
嗨,谢谢!看起来Ospray也改变了他们的函数,现在他们正在使用四元数... - Martín Maas
谢谢更新,我刚刚查看了Ospray的前一个版本ArcballCamera.cpp 的更新链接,看起来他们使用了一些定制矩阵库,看起来有点难以理解。然而,我发现Twinklebear的实现简单易懂,仅使用glm库。 - ollydbg23

0

我找到了一种非常便宜且不太正规的方法来实现它。 在OpenGL中,矩阵如下所示:
0  1  2  3
4  5  6  7
8  9  10 11
12 13 14 15

如果您将3个垂直数字作为旋转向量,则会获得所需的效果。例如:

如果您围绕向量(0、4、8)旋转,则会绕x轴旋转。
同样,(1、5、9)将是y,(3、6、10)将是z。
为什么它有效?……当您取垂直数字时,您制作了原始矩阵的转置副本,这使您与旋转矩阵相比具有某种起点矩阵……抱歉,我不是数学家,但它确实有效。相信我 ;)

我的OpenGL ES示例代码:

        Matrix.rotateM(Settings.anchorMatrix, 0, rX, Settings.anchorMatrix[0], Settings.anchorMatrix[4], Settings.anchorMatrix[8]);
        Matrix.rotateM(Settings.anchorMatrix, 0, rY, Settings.anchorMatrix[1], Settings.anchorMatrix[5], Settings.anchorMatrix[9]);
        Matrix.rotateM(Settings.anchorMatrix, 0, rZ, Settings.anchorMatrix[2], Settings.anchorMatrix[6], Settings.anchorMatrix[10]);

anchorMatrix 技术上是我为了跟踪记录而在旁边创建的主矩阵。Android OpenGL ES 不允许我从状态机中获取当前矩阵。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接