OpenGL. Почему скорость камеры меняется в зависимости от направления камеры?

Рейтинг: 1Ответов: 0Опубликовано: 29.04.2023

Поворот камеры отлично работает. Проблема в перемещении камеры клавишами WASD. Обработка клавиш происходит в конце кода в главном цикле, используя метод GetAsyncKeyState.

Но поведение камеры неочевидное! Вот перечень проблем:

  1. Случай, если не трогать камеру. Если нажать любую из клавиш, камера начнёт перемещаться и через секунду немного увеличит свою скорость.
  2. Случай, если перемещать камеру. Если одновременно идти вперёд и в сторону, и перемещать мышь, то скорость камеры так же увеличится.

Возможно я немного непонятно или неправильно объяснил, поэтому вот ссылка на готовую для запуска программу. https://disk.yandex.ru/d/cS3pcoG5OlUzHw

Мне надо, чтобы скорость перемещения камеры была постоянной и не изменялась в зависимости от направления камеры.

Исходный код:

#include <iostream>

#define SDL_MAIN_HANDLED
#include <SDL.h>

#define GLEW_STATIC
#include <GL/glew.h>

#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>

#include <Windows.h>
#include <chrono>
#include <thread>

#define WINDOW_WIDTH 800
#define WINDOW_HEIGHT 600

GLuint shaderProgram;

glm::vec3 camPos(0, 0, 6);
glm::vec3 camFront(0, 0, -1);
glm::vec3 camUp(0, 1, 0);

float camSpeed = 0.001;

float lastX = -1;
float lastY = -1;

float yaw = -90.0f;
float pitch = 0.0f;

void updateCamera(float mouseX, float mouseY)
{
    if (lastX == -1 && lastY == -1)
    {
        lastX = mouseX;
        lastY = mouseY;
        return;
    }

    float xOffset = mouseX - lastX;
    float yOffset = lastY - mouseY;
    lastX = mouseX;
    lastY = mouseY;
    float sensitivity = 0.1;
    xOffset *= sensitivity;
    yOffset *= sensitivity;
    yaw += xOffset;
    pitch += yOffset;
    glm::vec3 direction;
    direction.x = cos(glm::radians(yaw)) * cos(glm::radians(pitch));
    direction.y = sin(glm::radians(pitch));
    direction.z = sin(glm::radians(yaw)) * cos(glm::radians(pitch));
    camFront = glm::normalize(direction);
}

void draw()
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glm::mat4 Projection = glm::perspective(glm::radians(45.0f), (float)WINDOW_WIDTH / (float)WINDOW_HEIGHT, 0.1f, 100.0f);
    glm::mat4 View = glm::lookAt(
        camPos,
        camPos + camFront,
        camUp
    );
    glm::mat4 Model = glm::mat4(1.0f);
    GLuint mvpId = glGetUniformLocation(shaderProgram, "mvp");
    glm::mat4 mvp = Projection * View * Model;
    glUniformMatrix4fv(mvpId, 1, GL_FALSE, &mvp[0][0]);
    GLuint modelId = glGetUniformLocation(shaderProgram, "model");
    glUniformMatrix4fv(modelId, 1, GL_FALSE, &Model[0][0]);
    GLuint lightPosId = glGetUniformLocation(shaderProgram, "lightPos");
    GLfloat lightPos[] = { 1.0f, 2.0f, 2.0f };
    glUniform3fv(lightPosId, 1, lightPos);

    glEnable(GL_DEPTH_TEST);
    glDrawArrays(GL_TRIANGLES, 0, 12);
    glDisable(GL_DEPTH_TEST);
}

int main()
{
    SDL_Init(SDL_INIT_VIDEO);
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
    SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
    SDL_Window* window = SDL_CreateWindow("OpenGL Draw", 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT, SDL_WINDOW_OPENGL);
    SDL_SetWindowPosition(window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
    SDL_GLContext context = SDL_GL_CreateContext(window);

    glewExperimental = GL_TRUE;
    glewInit();

    GLuint vao;
    glGenVertexArrays(1, &vao);
    glBindVertexArray(vao);

    float vertices[] =
    {   // position | color | normal
        -0.5f, -0.5f, 0.5f, 1, 1, 0, 0.0f, 0.0f, 1.0f, // front face
        -0.5f, 0.5f, 0.5f, 1, 1, 0, 0.0f, 0.0f, 1.0f,
        0.5f, 0.5f, 0.5f, 1, 1, 0, 0.0f, 0.0f, 1.0f,
        -0.5f, -0.5f, 0.5f, 1, 1, 0, 0.0f, 0.0f, 1.0f,
        0.5f, 0.5f, 0.5f, 1, 1, 0, 0.0f, 0.0f, 1.0f,
        0.5f, -0.5f, 0.5f, 1, 1, 0, 0.0f, 0.0f, 1.0f,

        0.5f, -0.5f, 0.5f, 1, 1, 0, 1.0f, 0.0f, 0.0f, // right face
        0.5f, 0.5f, 0.5f, 1, 1, 0, 1.0f, 0.0f, 0.0f,
        0.5f, 0.5f, -0.5f, 1, 1, 0, 1.0f, 0.0f, 0.0f,
        0.5f, -0.5f, 0.5f, 1, 1, 0, 1.0f, 0.0f, 0.0f,
        0.5f, 0.5f, -0.5f, 1, 1, 0, 1.0f, 0.0f, 0.0f,
        0.5f, -0.5f, -0.5f, 1, 1, 0, 1.0f, 0.0f, 0.0f,
    };
    GLuint vbo;
    glGenBuffers(1, &vbo);
    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    unsigned int indices[] =
    {
        0, 1, 2,
        2, 3, 0
    };
    GLuint ebo;
    glGenBuffers(1, &ebo);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

    const char* vertexSource = R"glsl(
        #version 150 core
        in vec3 position;
        in vec3 color;
        in vec3 normal;
        out vec3 Color;
        out vec3 Normal;
        out vec3 FragPos;
        uniform mat4 model;
        uniform mat4 mvp;
        void main()
        {
            Color = color;
            Normal = mat3(transpose(inverse(model))) * normal;
            FragPos = vec3(model * vec4(position, 1.0));
            gl_Position = mvp * vec4(position, 1.0);
        }
    )glsl";
    GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vertexShader, 1, &vertexSource, NULL);
    glCompileShader(vertexShader);

    const char* fragmentSource = R"glsl(
        #version 150 core
        in vec3 Color;
        in vec3 Normal;
        in vec3 FragPos;
        out vec4 outColor;
        uniform vec3 lightPos;
        void main()
        {
            vec3 lightColor = vec3(1.0, 1.0, 1.0);
            float ambientStrength = 0.1;
            vec3 ambient = ambientStrength * lightColor;
            vec3 norm = normalize(Normal);
            vec3 lightDir = normalize(lightPos - FragPos);
            float diff = max(dot(norm, lightDir), 0.0);
            vec3 diffuse = diff * lightColor;
            vec3 result = (ambient + diffuse) * Color;
            outColor = vec4(result, 1.0);
        }
    )glsl";
    GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragmentShader, 1, &fragmentSource, NULL);
    glCompileShader(fragmentShader);

    shaderProgram = glCreateProgram();
    glAttachShader(shaderProgram, vertexShader);
    glAttachShader(shaderProgram, fragmentShader);
    glLinkProgram(shaderProgram);
    glUseProgram(shaderProgram);

    GLint posAttrib = glGetAttribLocation(shaderProgram, "position");
    glVertexAttribPointer(posAttrib, 3, GL_FLOAT, GL_FALSE, 9 * sizeof(GL_FLOAT), 0);
    glEnableVertexAttribArray(posAttrib);
    GLint colorAttrib = glGetAttribLocation(shaderProgram, "color");
    glVertexAttribPointer(colorAttrib, 3, GL_FLOAT, GL_FALSE, 9 * sizeof(GL_FLOAT), (void*)(3 * sizeof(GL_FLOAT)));
    glEnableVertexAttribArray(colorAttrib);
    GLint normalAttrib = glGetAttribLocation(shaderProgram, "normal");
    glVertexAttribPointer(normalAttrib, 3, GL_FLOAT, GL_FALSE, 9 * sizeof(GL_FLOAT), (void*)(6 * sizeof(GL_FLOAT)));
    glEnableVertexAttribArray(normalAttrib);

    unsigned int last = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
    SDL_Event windowEvent;
    while (true)
    {
        unsigned int current = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
        unsigned int elapsedTime = current - last;

        if (GetAsyncKeyState(0x57) & 0x8000) // W
            camPos += elapsedTime * camSpeed * camFront;
        else if (GetAsyncKeyState(0x53) & 0x8000) // S
            camPos -= elapsedTime * camSpeed * camFront;
        if (GetAsyncKeyState(0x41) & 0x8000) // A
            camPos -= elapsedTime * camSpeed * glm::normalize(glm::cross(camFront, camUp));
        else if (GetAsyncKeyState(0x44) & 0x8000) // D
            camPos += elapsedTime * camSpeed * glm::normalize(glm::cross(camFront, camUp));

        if (SDL_PollEvent(&windowEvent))
        {
            if (windowEvent.type == SDL_QUIT) break;

            POINT mousePos;
            GetCursorPos(&mousePos);
            updateCamera(mousePos.x, mousePos.y);
        }
        else
        {
            int sleepTime = 1000 / 60 - elapsedTime;
            std::this_thread::sleep_for(std::chrono::milliseconds(sleepTime));
            draw();
            SDL_GL_SwapWindow(window);
            last = current;
        }
    }
    SDL_GL_DeleteContext(context);
    SDL_Quit();
    return 0;
}

Ответы

Ответов пока нет.