Двигаем объекты

При переносе объекта из одних координат в другие у меня не было понимания, как именно это происходит. Был лишь вызов функции переноса:


    mat4.translate(mvMatrix, [-1.5, 0.0, -7.0]);

И затем всю магию делал вершинный шейдер


    gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);

Давайте на время «отключим» эту магию и разберемся, что именно кроется за занавесом матриц.

Если вы взглянете на исходный код, то заметите, что в нем нет подключения скрипта gl-matrix. Все потому, что матрицы на стороне JavaScript мы использовать не будем. Вместо этого для управления переносом мы будем использовать uniform-переменную uTranslation. А вот и весь код вершинного шейдера:


    attribute vec3 aVertexPosition;

    uniform vec2 uTranslation;

    void main(void) {
        gl_Position = vec4(aVertexPosition, 1.0) + vec4(uTranslation, 0.0, 0.0);
    }

Итак, мы видим, что текущие координаты рассчитываются как исходные координаты плюс некоторое смещение. Такое поведение можно выразить формулой:

  • X’ = X + dX
  • Y’ = Y + dY

Здесь X и Y — исходные координаты, которые не меняются со временем, dX и dY — смещение, которое управляется на стороне JavaScript. Стоит заметить, что uTranslation — вектор размерности 2, поэтому мы дополняем его нулями до размерности 4.

Вот и весь вершинный шейдер. Он вышел довольно простым, так как расчет смещения происходит в JavaScript, а в шейдер передается лишь результат. Теперь взглянем на код JavaScript.

Перемещать объект сцены мы будем при помощи мыши. Поэтому инициализируем события:


    function initEvents() {
        var canvas = document.getElementById("canvas");
        document.onmousemove=onMouseMove;
        document.onmouseup=onMouseUp;
        canvas.onmousedown=onMouseDown;
    }

Далее зададим переменные, которыми будем регулировать перемещение объекта:


    var dragStartX = 0;
    var dragStartY = 0;
    var dragOffset = [0, 0];
    
    var mousePressed = false;
    
    var translation = [0, 0];

В mousePressed мы отмечаем, что перетягивание началось (над canvas была зажата кнопка мыши), в dragStartX и dragStartY мы запоминаем координаты зажатия кнопки, а в dragOffset хранится значение того, насколько мы перетянули объект с момента последнего нажатия кнопки мыши. Также мы храним текущее смещение объекта translation, которое сформировалось после всех наших предыдущих перетягиваний.

Далее обработчик нажатия кнопки:


    function onMouseDown(evt){
        dragStartX = evt.clientX;
        dragStartY = evt.clientY;
        dragOffset = [0, 0];
        mousePressed = true;
    }

Здесь мы запоминаем, где была зажата кнопка мыши, и устанавливаем флаг начала перетягивания. После зажатия кнопки начинается само перетягивание, которое обрабатывается в функции onMouseMove:


    function onMouseMove(evt) {
        if (mousePressed) {
            var diffX = evt.clientX - dragStartX;
            var diffY = dragStartY - evt.clientY;
            dragOffset = [diffX * 2 / 500, diffY * 2 / 500];
            var finalTranslation = [translation[0] + dragOffset[0], translation[1] + dragOffset[1]]
            gl.uniform2fv(shaderProgram.translationUniform, finalTranslation);
        }
    }

В этой функции мы проверяем, зажата ли кнопка мыши, и если да, то рассчитываем, насколько сместился курсор по соответствующим координатам в переменных diffX и diffY. Далее мы преобразуем смещение браузера в смещение внутри сцены (на этом остановимся подробнее немного ниже). Далее мы рассчитываем итоговое смещение finalTranslation на основе смещения от всех предыдущих перетягиваний плюс текущее смещение, и передаем это итоговое смещение в шейдер.

Итак, как же происходит преобразование координат браузера в координаты сцены? Для этого нам нужно знать размеры canvas и размеры сцены. Размеры canvas мы знаем — это квадрат размером 500х500. А сцена, так как мы не перемещали камеру и вообще не делали ничего кроме отрисовки объекта, занимает пространство от -1 до 1 (и по горизонтали, и по вертикали). Получается, что сцена тоже квадратная и имеет размер 2х2. Отсюда получается, что один пиксель браузера равен 2/500 части сцены.

И, наконец, обработчик отпускания мыши, где мы обновляем смещение и сбрасываем флаг перетягивания:


    function onMouseUp(evt){
        if (mousePressed) {
            translation = [translation[0] + dragOffset[0], translation[1] + dragOffset[1]];
            mousePressed = false;
        }
    }

Готово! Теперь можно открыть страницу демо или скачать пример и подвигать объект по сцене.

Итак, пока мы отлично обошлись без матриц — достаточно было лишь прибавлять смещение к исходным координатам объекта. Далее мы посмотрим, сможем ли мы масштабировать и поворачивать объект без участия матриц. Возможно, человечество идет не тем путем, и матрицы лишь усложняют нам жизнь :)?..

Двигаем объекты
Метки:

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *