cherry.png

Вращаем куб в пространстве. Canvas

05.08.2017
checkmark3

HTML5, Canvas

Рассматриваем разницу между параллельной и перспективной проекцией. Обе широко используются на практике для различных целей.
В предыдущемпримеремы вращали квадрат на плоскости. Переходим в трехмерное пространство. Теперь, чтобы отобразить на плоскости экрана движение трехмерного объекта, нужно сначала создать трехмерный объект, повернуть его на угол, срисовать с него проекцию и отобразить на экране уже проекцию.

Ваш браузер не поддерживает Canvas

Параллельная проекция

Вид проекции, которая образуется с помощью параллельных проецирующих лучей.

Ваш браузер не поддерживает Canvas

Перспективная проекция

Вид проекции, которая образуется с помощью проецирующих лучей, исходящих из одной точки.
У этой статьи есть продолжение:Вращаем пространственный крест

JavaScript

    'use strict';

    // Рисовать будем сразу две картинки,
    // объект у нас будет один, а проеций будет много
    var canvas1 = document.getElementById('canvas1');
    var canvas2 = document.getElementById('canvas2');

    var canvas_context1 = canvas1.getContext('2d');
    var canvas_context2 = canvas2.getContext('2d');

    // Сместим начало координат в центр блока
    canvas_context1.translate(canvas1.width / 2, canvas1.height / 2);
    canvas_context2.translate(canvas2.width / 2, canvas2.height / 2);
    // Фигуру будем задавать как совокупность четырех плоскостей,
    // каждая плоскость - совокупность четырех точек
    var arr_t = [
    // Передняя стенка
    [{x:-100, y:-100, z:-100},
        {x:100, y:-100, z:-100},
        {x:100, y:100, z:-100},
        {x:-100, y:100, z:-100}],
    // Правая стенка
    [{x:100, y:-100, z:-100},
        {x:100, y:-100, z:100},
        {x:100, y:100, z:100},
        {x:100, y:100, z:-100}],
    // Левая стенка
    [{x:-100, y:-100, z:-100},
        {x:-100, y:-100, z:100},
        {x:-100, y:100, z:100},
        {x:-100, y:100, z:-100}],
    // Задняя стенка
    [{x:-100, y:-100, z:100},
        {x:100, y:-100, z:100},
        {x:100, y:100, z:100},
        {x:-100, y:100, z:100}]];
    // Центр фигуры.
    // Вокуг него будем осуществлять поворот
    var t0 = {x:0, y:0, z:0};

    // Угол поворота в градусах
    var deg = 0;

    // Для перспективной проекции:
    // Удаленность наблюдателя от объекта.
    // Не может быть меньше размеров объекта,
    // в нашем случае 200px
    var a = 300;

    // Размер объекта на экране в (%)
    var k = 70;

    // Текущая фигура
    var arr_tt = [];
    for (var i = 0; i < arr_t.length; i++) {
        arr_tt[i] = arr_t[i].slice();
    }

    // Текущая проекция
    var arrProj;
    // Задаем функцию обновления отображения
    // с поворотом фигуры.
    // Затем эту функцию надо будет вставить
    // в переменную window.onload
    function repaint() {

        // Сначала очистим экран - закрасим белым цветом
        clearScreen(canvas1, canvas_context1, 'rgb(255,255,255)');
        clearScreen(canvas2, canvas_context2, 'rgb(255,255,255)');

        // Теперь повернем исходную фигуру на угол
        // по оси Y и перезапишем текущую фигуру
        for (var i = 0; i < arr_t.length; i++) {
            for (var j = 0; j < arr_t[0].length; j++) {
                arr_tt[i][j] = rotateOnDegreeY(t0, arr_t[i][j], deg);
            }
        }

        // Получим параллельную проекцию
        arrProj = getParallelProjection(arr_tt);

        // Нарисуем ее
        canvas_context1.lineWidth = 2; // черный полупрозрачный
        strokeFigure(canvas_context1, arrProj, 'rgba(0,0,0,0.5)');

        // Получим перспективную проекцию
        arrProj = getPerspectiveProjection(arr_tt);

        // Нарисуем ее тоже в соседнем окошке
        canvas_context2.lineWidth = 2; // черный полупрозрачный
        strokeFigure(canvas_context2, arrProj, 'rgba(0,0,0,0.5)');

        // Увеличиваем угол
        deg += 1;
    }
    // Далее идут вспомогательные функции

    // Получаем параллельную проекцию фигуры на плоскость экрана
    function getParallelProjection(arr) {

        var arr_new = [];

        for (var i = 0; i < arr.length; i++) {
            arr_new[i] = [];
            for (var j = 0; j < arr[0].length; j++) {
                arr_new[i][j] = {};
                arr_new[i][j].x = arr[i][j].x;
                arr_new[i][j].y = arr[i][j].y + arr[i][j].z / 4;
            }
        }

        return arr_new;
    }
    // Получаем перспективную проекцию
    function getPerspectiveProjection(arr) {
        var arr_new = [];

        for (var i = 0; i < arr.length; i++) {
           arr_new[i] = [];
           for (var j = 0; j < arr[0].length; j++) {
              arr_new[i][j] = {};
              arr_new[i][j].x = (a / 100 * k) * arr[i][j].x / (arr[i][j].z + a);
              arr_new[i][j].y = (a / 100 * k) * arr[i][j].y / (arr[i][j].z + a);
           }
        }

        return arr_new;
    }
    // Закрашиваем весь экран определенным цветом
    function clearScreen(canvas, context, color) {
        context.fillStyle = color;

        context.beginPath();
        context.fillRect(- canvas.width / 2,
                         - canvas.height / 2,
                           canvas.width,
                           canvas.height);
        context.closePath();
        context.fill();
    }

    // Рисуем фигру по точкам из массива
    function strokeFigure(context, arr, color) {
        context.strokeStyle = color;
        context.fillStyle = color;

        for (var i = 0; i < arr.length; i++) {
            context.beginPath();
            for (var j = 0; j < arr[0].length; j++) {
                if (j == 0) {
                    context.moveTo(arr[i][j].x, arr[i][j].y);
                } else {
                    context.lineTo(arr[i][j].x, arr[i][j].y);
                }
            }
            context.closePath();
            context.stroke();
        }
    }
    // Поворачиваем точку t(x,y,z) на угол (deg) по оси (Y)
    // относительно точки t0(x,y,z)
    function rotateOnDegreeY(t0, t, deg) {

        var t_new = {};

        // Переводим угол поворота из градусов в радианы
        var rad = (Math.PI / 180) * deg;

        // Рассчитываем координаты новой точки по формуле
        t_new.x = t0.x+(t.x-t0.x)*Math.cos(rad)-(t.z-t0.z)*Math.sin(rad);
        t_new.y = t.y;
        t_new.z = t0.z+(t.x-t0.x)*Math.sin(rad)+(t.z-t0.z)*Math.cos(rad);

        // Возвращаем полученное значение
        return t_new;
    }

    // Устанавливаем интервал обновления отображения
    window.onload = function() {
        setInterval(repaint, 100);
    };
That's all folks! В дополнение приведу поворот по всем трем осям сразу.
Параллельная проекция

Ваш браузер не поддерживает Canvas

Перспективная проекция

Ваш браузер не поддерживает Canvas


facebookvkontaktetwitterodnoklassnikimailrulivejournal

Комментарии

O0O0O0O0
Комментатор
01.01.1970 03:00 (MSK)
Комментариев пока нет.. Вы можете стать первым..