如何使SDL2鼠标运动使3D相机无限旋转?

时间:2018-01-20 11:12:17

标签: c++ opengl 3d camera sdl-2

我正在尝试实现3D相机鼠标旋转。现在我所能做的只是旋转,如果我的鼠标在窗口的边界。这意味着如果我的窗口是1280宽度,如果鼠标坐标丰富0或1279,我就无法旋转更多。我读过相对鼠标应该有帮助。但它对我不起作用。

在主循环之前

SDL_ShowCursor(SDL_DISABLE);
SDL_SetRelativeMouseMode(SDL_TRUE);
SDL_WarpMouseInWindow(window, SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2);

旋转xyz的函数

glm::vec3 MouseMotion(float x, float y)
{
    float lastX = SCREEN_WIDTH / 2, lastY = SCREEN_HEIGHT / 2;
    float pitch = 0.0f, yaw = -90.0f;

    float offsetX = x - lastX;
    float offsetY = lastY - y;
    lastX = x;
    lastY = y;

    float sensitivity = 0.3f;

    yaw += offsetX * sensitivity;
    pitch += offsetY * sensitivity;

    if (pitch > 89.0f)
        pitch = 89.0f;
    if (pitch < -89.0f)
        pitch = -89.0f;

    glm::vec3 front;
    front.x = std::cos(glm::radians(yaw)) * std::cos(glm::radians(pitch));
    front.y = std::sin(glm::radians(pitch));
    front.z = std::sin(glm::radians(yaw)) * std::cos(glm::radians(pitch));

    //std::cout << "Mouse: " << offsetX << ", " << offsetY << std::endl;
    //std::cout << "YAW AND PITCH: " << yaw << ", " << pitch << std::endl;

    return glm::normalize(front);
}

和事件循环

while (SDL_PollEvent(&event))
    {
        if (event.type == SDL_QUIT)
        {
            exit(0);
        }
        if (event.type == SDL_MOUSEMOTION)
        {
            float mouseX = (float)event.motion.x;
            float mouseY = (float)event.motion.y;
            cameraFront = MouseMotion(mouseX, mouseY);
        }
    }

它正在工作,但只有当我的鼠标在边界内时才会有效。

更改为event.motion.xrel和event.motion.yrel不会解决此问题。由于我的相机抖动,即向右移动并返回中心或左侧并返回中心,因此它更容易被窃听。

如何让我的相机完全移动,无限移动?

1 个答案:

答案 0 :(得分:0)

继续评论之后,这里列出了一系列问题:

  • 您需要在SDL_WarpMouseInWindow事件后每次调用SDL_MOUSEMOTION来重置鼠标,否则它会很快达到您所说的“界限”。

  • pitchyaw的范围必须高于游戏循环,否则每次都会重置它们,您将无法实现“无限循环”。 / p>

  • sensitivity 太高了。典型的~500单位的鼠标移动转换为〜180度转动。

这是尝试修复上述问题,并进行了一些代码重构:

glm::vec3 polarVector(float p, float y)
{
    // this form is already normalized
    return glm::vec3(std::cos(y) * std::cos(p),
                     std::sin(p),
                     std::sin(y) * std::cos(p));
}

// clamp pitch to [-89, 89]
float clampPitch(float p)
{
    return p > 89.0f ? 89.0f : (p < -89.0f ? -89.0f : p);   
}

// clamp yaw to [-180, 180] to reduce floating point inaccuracy
float clampYaw(float y)
{
    float temp = (y + 180.0f) / 360.0f;
    return y - ((int)temp - (temp < 0.0f ? 1 : 0)) * 360.0f;
}



int main(int argc, char*args[])
{
    // ...

    // experiment with this
    const float sensitivity = 0.001f; 

    #define CTR_X (SCREEN_WIDTH / 2)
    #define CTR_Y (SCREEN_HEIGHT / 2)
    #define RESET_MOUSE SDL_WarpMouseInWindow(window, CTR_X, CTR_Y)

    // call once at the start
    RESET_MOUSE;

    // keep outside the loop
    float pitch = 0.0f, yaw = 0.0f;

    while (!quit)
    {
        // ...

        while (SDL_PollEvent(&event))
        {
            if (event.type == SDL_QUIT)
            {
                exit(0);
            }
            if (event.type == SDL_MOUSEMOTION)
            {
                float deltaX = (float)event.motion.x - CTR_X;
                float deltaY = (float)event.motion.y - CTR_Y;

                yaw = clampYaw(yaw + sensitivity * deltaX);
                pitch = clampPitch(pitch - sensitivity * deltaY);

                // assumes radians input
                cameraFront = polarVector(glm::radians(pitch), glm::radians(yaw));

                // reset *every time*
                RESET_MOUSE;
            }
        }

        // ...
    }
}