Страницы

Поиск по вопросам

Показаны сообщения с ярлыком 3d. Показать все сообщения
Показаны сообщения с ярлыком 3d. Показать все сообщения

среда, 15 апреля 2020 г.

С чего начинать для рисования?

#python #3d

                    
Надо создать программу в которой должно отображаться 3 фигуры со стереометрии (Куб,
Пирамида, Сфера). Размеры - любые, условие чтобы они крутились. Кто может помогите
мне кто чем может. Я не прошу выполнять за меня задание. Я бы хотел чтобы могли накидать
наброски или ссылки с примером.  

Доп. информация:
Язык: python sage;
Работал в языках: php, pascal, чуток с++;
Знание языка: Даже не вкурсе как его компилировать и где запускать.
    


Ответы

Ответ 1



Простые геометрические фигуры достаточно просто рисовать и анимировать в VPython (Python + 3D графика). Хорошие 2D и 3D графики рисуются в PyX. Очень хорошие 2D и 3D графики рисуются в matplotlib. Плюс есть еще очень много графических библиотек на любой вкус. Проще всего начать, пожалуй, с какого-нибудь руководства по VPython.

Ответ 2



А использовать 3д-редакторы не вариант ? Просто сейчас много open-source проектов и можно их использовать почти в любой сфере

вторник, 25 февраля 2020 г.

3D-вращение без jQuery (jQuery 3D plugin)

#javascript #html #css #3d


Существует отличный плагин (http://www.zachstronaut.com/projects/rotate3di/#demos)
для вращения элементов разметки. Использование плагина весьма просто и удобно
$('#something').click(function () {
    $(this).rotate3Di('-=180', 1000);
});

где #something - это тот элемент, который нужно вращать по клику. Однако возникла
необходимость вращать элемент без подключения внешних библиотек, вроде jQuery. Как
это сделать одним HTML5/CSS3/JavaScript?    


Ответы

Ответ 1



Вариант №1 - изучить исходники http://www.zachstronaut.com/js/rotate3di.js и почитать инфу тут: http://www.zachstronaut.com/projects/rotate3di/#demos Используется transform вкупе со skew и scale. Нужно будет переписать анимацию на setTimeout (а лучше на requestAnimationFrame + setTimeout) и просто можно заюзать формулы преобразований их исходника. Вариант №2 - заняться изучением css3 более подробно: http://www.html5rocks.com/en/tutorials/3d/css/ http://www.netmagazine.com/features/20-stunning-examples-css-3d-transforms

Ответ 2



Работай с матрицами напрямую, смотри доки к функциям OpenGL. Зачем? Затем, что там заодно описана матрица. Нужна матрица вращения? Пожалуйста - http://www.opengl.org/sdk/docs/man/xhtml/glRotate.xml , там есть матрица, хромом не отображает, так что юзай Oper'y. Напиши функцию перемножения матрицы на вектор. Кстати, матрицы, описываемые на том сайте, ориентированы по столбцам (прошу прощения за некорректное выражение), т.е. чтобы получить результирующий вектор - необходимо именно матрицу умножить на вектор-столбец. PS. Посмотрел, там не хватает перспективы! Так что матрицы - это ваше все. Цените их, пока они не обиделись.

Ответ 3



front
back
Для chrome пример : http://jsfiddle.net/zNvP6/

среда, 22 января 2020 г.

Деформация 3D модели с помощью кривых Безье

#cpp #3d #directx #mesh


Пишу программу, которая должна деформировать модель по опорным точкам. 

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



//вычисляем полином Бершнейна третьей степени на основании четырех точек
XMFLOAT3 Bernstein(float u, XMFLOAT3 p1, XMFLOAT3 p2, XMFLOAT3 p3, XMFLOAT3 p4) {
    XMFLOAT3 a, b, c, d;

    a = pointTimes(pow(u, 3), p1);
    b = pointTimes(3 * pow(u, 2) * (1 - u), p2);
    c = pointTimes(3 * u * pow((1 - u), 2), p3);
    d = pointTimes(pow((1 - u), 3), p4);

    return pointAdd(pointAdd(a, b), pointAdd(c, d));
}

//тоже самое что и верхняя функция, только принимаем указатель на массив точек
XMFLOAT3 Bernstein(float u, XMFLOAT3 *p) {
    XMFLOAT3 a, b, c, d;

    a = pointTimes(pow(u, 3), p[0]);
    b = pointTimes(3 * pow(u, 2) * (1 - u), p[1]);
    c = pointTimes(3 * u * pow(1 - u, 2), p[2]);
    d = pointTimes(pow(1 - u, 3), p[3]);

    return pointAdd(pointAdd(a, b), pointAdd(c, d));
}

void CreateSurfaceByControlPoints(XMFLOAT3 points[], D3DTextureVertex **ppVertices) {
    const int step = 16;    //количество делений у полотна
                            //чем больше делений, тем глаже модель становится (создается
больше вершин)

    const int max = (step * (step + 1) * 2);    //общее количество вершин у модели

    XMFLOAT3 temp[4];
    XMFLOAT3 *last = new XMFLOAT3[step + 1];

    //в массиве points, размером 16 точек,
    //у меня верхняя линия с индексами от 0 до 3, следющая от 4 до 7 и т.д.

    for(int i = 0; i < 4; i++) {
        temp[i] = points[(i + 1) * 4 - 1];
    }

    for(int i = 0; i < step + 1; i++) {
        last[i] = Bernstein((float)i / (float)step, temp);
    }

    *ppVertices = new D3DTextureVertex[max];

    int c = 0; //счетчик текущей вершины

    for(int i = 1; i < step + 1; i++) {
        float py = (float)i / (float)step;
        float pyold = ((float)i - 1.0f) / (float)step;

        for(int j = 0; j < 4; j++) {
            temp[j] = Bernstein(py, points[j * 4 + 0], points[j * 4 + 1], points[j
* 4 + 2], points[j * 4 + 3]);
        }

        for(int j = 0; j <= step; j++) {
            float px = static_cast(j) / (float)step;

            (*ppVertices)[c++] = D3DTextureVertex(last[j].x, last[j].y, last[j].z,
pyold, px);

            last[j] = Bernstein(px, temp);

            (*ppVertices)[c++] = D3DTextureVertex(last[j].x, last[j].y, last[j].z,
py, px);
        }
    }

    delete[] last;
}


Все вышесказанное работает отлично, но теперь задача усложнилась и мне нужно загружать
произвольную модель (она тоже 2D, как и моя процедурная), добавлять опорные точки таким
же образом и деформировать меш по такому же подобию.

Модель уже имеет свою геометрию и UV координаты.
Выглядит следующим образом:


Главный вопрос - есть ли какая-нибудь формула, чтобы рассчитать позицию для каждого
вертекса, основываясь на 8 кривых Безье (4 горизонтальных и 4 вертикальных), учитывая
их первоначальную позицию? Как, например, в профессиональных программах Maya или Blender
есть такие инструменты, как произвольно расставить опорные точки и делать с моделью
любые деформации.
    


Ответы

Ответ 1



Спасибо за помощь! Получилось решить проблему следующим образом: //1378 и 861 - значения у меня рассчитаны, чтобы вся модель умещалась на экране //у вас они могут быть другие или вообще по-другому считаться, главное, чтобы pz и px были в пределах [0.0f, 1.0f] //в переменной points хранятся опорные точки //в ppOutVertices хранится указатель на вершины, которые будем задавать //в ppBaseVertices хранится массив вершин по-умолчанию, на основе которых формируем новый массив //nVertices - количество вершин в моделе void UpdateSurface(XMFLOAT3 points[], D3DTextureVertex **ppOutVertices, D3DTextureVertex **ppBaseVertices, UINT nVertices) { XMFLOAT3 tmp[4]; *ppOutVertices = new D3DTextureVertex[nVertices]; for(int i = 0; i < nVertices; i++) { float pz = (float)(*ppBaseVertices)[i].Pos.x / (1378.0f / 100.0f); for(int j = 0; j < 4; j++) { tmp[j] = Bernstein(pz, points[j * 4 + 0], points[j * 4 + 1], points[j * 4 + 2], points[j * 4 + 3]); } float px = (float)(*ppBaseVertices)[i].Pos.z / (861.0f / 100.0f); XMFLOAT3 bern = XMFLOAT3(Bernstein(px, tmp)); (*ppOutVertices)[i].Pos = XMFLOAT3(bern.x, 0.0f, bern.z); } }

Конвертация 3D моделей в XAML

#xaml #3d


Имеется порядка 30-ти 3D моделей, нарисованных в 3DMax (соответственно исходный формат
любой, поддерживаемый в 3DMax: .max, .3ds и т.д.).
Требуется конвертировать данные 3D модели в XAML разметку, желательно, в виде статических
ресурсов.
Какими бесплатными средствами (программы, скрипты, плагины и т.д.) проще всего осуществить
конвертацию и получить наилучший результат?    


Ответы

Ответ 1



Задача не из простых, но решение имеется: 3d model to WPF

воскресенье, 12 января 2020 г.

Как сделать просмотр 3д моделей на сайте?

#javascript #3d


Как реализована функция просмотра 3d моделей на этом сайте http://armory.warmane.com/character/Apparent/Lordaeron/summary
, есть мб какие-то библиотеки js по 3д моделированию или готовые решения?
    


Ответы

Ответ 1



Для просмотра 3D моделей, сперва их надо загрузить (или импортировать) в приложение. Импортирование подразумаевает банальное считывание данных из файла содержащего все необходимые данные, а именно: Аттрибуты вершины (vertex attributes) позиции вершин в локальной системе координат (x, y, z) нормалей к вершинам (x, y, z) текстурные координаты (x, y), 0 <= x, y <= 1 Информацию о материалах модели (Ambient, Diffuse, Specular составляющие каждого материала) Ambient Ka - степень материала воспринимать фоновый свет. Diffuse Kd - (рассеивающая) составляющая материала, показывает степень материала воспринимать рассеивающий свет - тот свет который в зависимости от ориентации (нормали) поверхности и направления света рассеивается во все стороны равномерно. Specular Ks - свойство материала воспринимать зеркальный свет Например, если представить себе модель какой-либо машины - то там обивка салона это один материал, шины другой материал, кузов вобще другой (например матовый), а диски блестят. кусок из .mtl файла # Blender MTL File: 'modelname.blend' # Material Count: 6 newmtl Arm Ns 96.078431 Ka 0.000000 0.000000 0.000000 Kd 0.640000 0.640000 0.640000 Ks 0.500000 0.500000 0.500000 Ni 1.000000 d 1.000000 illum 2 map_Kd arm_dif.png map_Bump arm_showroom_ddn.png map_Ks arm_showroom_spec.png // ... Текстуры - изображения (images) в каком либо формате для текстурирования с помощью текстурных координат. В случае с js импортированием будет разбор (или парсинг) файла экспортированного из какого - либо 3D редактора содержащего данные (аттрибуты вершин, текстуры) о модели. Этот файл сервер должен отдавать клиенту в виде plain/text или application/json или application/javascript. Есть решения которые делают всю рутинную работу связанную с считыванинем (или парсингом), и как уже упомянули о three.js добавлю, что он включает в себя подобные решения (инструменты или loaderы) некоторые из них: OBJLoader MTLLoader ColadaLoder glTFloader JSONLoader MaterialLoader Если вершины с аттрибутами в .obj файле то обычно инфа о материалах и текстуры идут в отдельном .mtl файле, потому и столько различных лоадеров.

суббота, 11 января 2020 г.

Как отрисовать объект с использованием буфера глубины, но без записи в него?

#3d #directx


Требуется отрендерить объект, причем он должен учитывать буфер глубины (т.е. не перекрывать
ближние объекты и перекрывать дальние), но при этом не менять значения в буфере глубины.
Интересует для DirectX (10 или 11).

Т.е. нужно то самое, что в OpenGL делает:

glDepthMask(GL_FALSE);

    


Ответы

Ответ 1



Все, разобрался. Нужно использовать флаг D3D10_DEPTH_WRITE_MASK_ZERO, сбило с толку его название - выглядит так, как будто он включает режим записи нулей.

четверг, 9 января 2020 г.

Удаленность сторон куба от плоскости проекции

#javascript #алгоритм #графика #3d #геометрия


Дано:


Система координат
Куб вращается вокруг своего центра по всем трем осям.
Угол поворота заранее не известен.
Параллельная проекция считается по формуле:


  x`` = x;
  
  y`` = y + z / 4;

В единицу времени отображаются только 3 ближних к плоскости проекции грани.


Вопрос:

Как определить, какие из граней ближние?

Делаю таким образом: беру попарно грани правую и левую, переднюю и заднюю, верхнюю
и нижнюю, затем вычисляю дальнюю и ближнюю точки по оси (Z), составляю уравнение прямой,
беру вторую по удаленности точку по оси (Z), и подставляю в это уравнение - так определяю
наклон плоскости. Исходя из этого выбираю какая из 2х граней ближе к плоскости проекции.

Такой код не работает. 



'use strict';

    var figures;

    var figure_111 = {

        //╔═══╗
        //║   ║
        //╚═══╝

        figureType: 5,
        figurePositionY: 1,
        figurePositionX: 1,
        figurePositionZ: 1,
        shape: [
            [[5]]
        ],
        rotateZ: function () {
            this.shape = rotateZ(this.shape);
            this.figurePositionZ = changePosition(this.figurePositionZ);
        }
    };

    function initAnimation() {
        figures = [
            figure_111
        ];

        // Запускаем падение
        figureStartsFalling();
    }

    // Текущая фигура
    var currentFigure;

    // Размер фигуры
    var size = 30;

    // Объект на основании фигуры
    var fallingObject;

    // Цвет фигуры
    var figureColor;

    // Центр фигуры
    var t0;

    var canvas = document.getElementById('animation');
    var canvas_context = canvas.getContext('2d');

    // Сместим центр оси (Х) в середину поля
    canvas_context.translate(canvas.width / 2, 0);

    var doTenTimes;

    function figureStartsFalling() {
        doTenTimes = 30;
        // Переопределяем текущую фигуру
        currentFigure = figures[Math.floor(Math.random() * figures.length)];

        // Получим цвет текущей фигуры
        figureColor = 'rgb(0,200,0)';

        // Высота фигуры
        var figureHeight = currentFigure.shape.length;

        // Строим 3Д объект на основании фигуры
        fallingObject = [];
        var cube = [];
        var side = [];

        for (var y = currentFigure.shape.length - 1; y > -1; y--) {
            for (var x = 0; x < currentFigure.shape[0].length; x++) {
                for (var z = 0; z < currentFigure.shape[0][0].length; z++) {
                    // Для каждой клеточки создаем кубик с 5 гранями
                    cube = [];
                    if (currentFigure.shape[y][x][z] != 0) {
                        // Левая стенка
                        side = [
                            {x:x*size, y:y*size - figureHeight*size, z:z*size},
                            {x:x*size, y:y*size - figureHeight*size, z:z*size + size},
                            {x:x*size, y:y*size + size - figureHeight*size, z:z*size
+ size},
                            {x:x*size, y:y*size + size - figureHeight*size, z:z*size},
                            {m:'left'}
                        ];
                        // Центральная координата, нужна для расчета удаленности
по осям (X) и (Z)
                        side[4] = getSidecentralCoordinate(side);
                        // Если левее чтото есть, то не рисуем
                        if (x > 0
                            && currentFigure.shape[y][x - 1][z] != 0) {
                            side[5] = false;
                        }
                        cube.push(side);

                        // Правая стенка
                        side = [
                            {x:x*size + size, y:y*size - figureHeight*size, z:z*size},
                            {x:x*size + size, y:y*size - figureHeight*size, z:z*size
+ size},
                            {x:x*size + size, y:y*size + size - figureHeight*size,
z:z*size + size},
                            {x:x*size + size, y:y*size + size - figureHeight*size,
z:z*size},
                            {m:'right'}
                        ];
                        side[4] = getSidecentralCoordinate(side);
                        // Если правее чтото есть, то не рисуем
                        if (x + 1 < currentFigure.shape[0].length
                            && currentFigure.shape[y][x + 1][z] != 0) {
                            side[5] = false;
                        }
                        cube.push(side);

                        // Задняя стенка
                        side = [
                            {x:x*size, y:y*size - figureHeight*size, z:z*size},
                            {x:x*size + size, y:y*size - figureHeight*size, z:z*size},
                            {x:x*size + size, y:y*size + size - figureHeight*size,
z:z*size},
                            {x:x*size, y:y*size + size - figureHeight*size, z:z*size},
                            {m:'rear'}
                        ];
                        side[4] = getSidecentralCoordinate(side);
                        // Если сзади чтото есть, то не рисуем
                        if (z > 0
                            && currentFigure.shape[y][x][z - 1] != 0) {
                            side[5] = false;
                        }
                        cube.push(side);

                        // Передняя стенка
                        side = [
                            {x:x*size, y:y*size - figureHeight*size, z:z*size + size},
                            {x:x*size + size, y:y*size - figureHeight*size, z:z*size
+ size},
                            {x:x*size + size, y:y*size + size - figureHeight*size,
z:z*size + size},
                            {x:x*size, y:y*size + size - figureHeight*size, z:z*size
+ size},
                            {m:'front'}
                        ];
                        side[4] = getSidecentralCoordinate(side);
                        // Если впереди чтото есть, то не рисуем
                        if (z + 1 < currentFigure.shape[0][0].length
                            && currentFigure.shape[y][x][z + 1] != 0) {
                            side[5] = false;
                        }
                        cube.push(side);

                        // Верхняя стенка
                        side = [
                            {x:x*size, y:y*size - figureHeight*size, z:z*size},
                            {x:x*size, y:y*size - figureHeight*size, z:z*size + size},
                            {x:x*size + size, y:y*size - figureHeight*size, z:z*size
+ size},
                            {x:x*size + size, y:y*size - figureHeight*size, z:z*size},
                            {m:'top'}
                        ];
                        side[4] = getSidecentralCoordinate(side);
                        // Если сверху чтото есть, то не рисуем
                        if (y > 0
                            && currentFigure.shape[y - 1][x][z] != 0) {
                            side[5] = false;
                        }
                        cube.push(side);

                        // Нижняя стенка
                        side = [
                            {x:x*size, y:y*size + size - figureHeight*size, z:z*size},
                            {x:x*size, y:y*size + size - figureHeight*size, z:z*size
+ size},
                            {x:x*size + size, y:y*size + size - figureHeight*size,
z:z*size + size},
                            {x:x*size + size, y:y*size + size - figureHeight*size,
z:z*size},
                            {m:'bottom'}
                        ];
                        side[4] = getSidecentralCoordinate(side);
                        // Если снизу чтото есть, то не рисуем
                        if (y + 1 < currentFigure.shape.length
                            && currentFigure.shape[y + 1][x][z] != 0) {
                            side[5] = false;
                        }
                        cube.push(side);
                        fallingObject.push(cube);
                    }
                }
            }
        }

        // Определим центр фигуры
        t0 = {
            y:currentFigure.shape.length / 2 * size - figureHeight*size,
            x:currentFigure.shape[0].length / 2 * size,
            z:currentFigure.shape[0][0].length / 2 * size
        };

        // Запускаем падение
        setTimeout(figureFalls, 100);
    }

    // Угол поворота, не изменяется
    var deg = 1;

    // Текущая проекция
    var arrProj;

    var tempArr;

    function figureFalls() {

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

        // Изменяем положение текущей фигуры
        for (var i = 0; i < fallingObject.length; i++) {
            for (var j = 0; j < 6; j++) {
                for (var k = 0; k < 4; k++) {
                    // Повернем фигуру на угол
                    fallingObject[i][j][k] = rotateOnDegreeY(t0, fallingObject[i][j][k]);
                    fallingObject[i][j][k] = rotateOnDegreeX(t0, fallingObject[i][j][k]);
                    fallingObject[i][j][k] = rotateOnDegreeZ(t0, fallingObject[i][j][k]);
                    // и опустим вниз на 1px
                    fallingObject[i][j][k].y += 1;
                }
                // Пересчитаем центральную координату плоскости
                fallingObject[i][j][4] = getSidecentralCoordinate(fallingObject[i][j]);
            }
            doTenTimes -= 1;
            // Пересчитаем центральную координату кубика
            fallingObject[i][6] = getCubecentralCoordinate(fallingObject[i]);
            // Если кубик ушел слишком далеко - удаляем кубик
            if (fallingObject[i][6].y > canvas.height) {
                fallingObject.splice(i, 1);
                i -=1;
            }
        }

        // Центр фигуры тоже опускается
        t0.y += 1;

        // Сортируем кубики по удаленности от начала координат по (X) и (Z)
        var exit = false;
        while (!exit) {
            exit = true;
            for (var i = 0; i < fallingObject.length - 1; i++) {
                if (fallingObject[i][6].z > fallingObject[i + 1][6].z) {
                    tempArr = fallingObject[i];
                    fallingObject[i] = fallingObject[i + 1];
                    fallingObject[i + 1] = tempArr;
                    exit = false;
                } else if (fallingObject[i][6].z == fallingObject[i + 1][6].z) {
                    if (fallingObject[i][6].x > fallingObject[i + 1][6].x) {
                        tempArr = fallingObject[i];
                        fallingObject[i] = fallingObject[i + 1];
                        fallingObject[i + 1] = tempArr;
                        exit = false;
                    }
                }
            }
        }

        // Отрисовываем фигуру
        for (var i = 0; i < fallingObject.length; i++) {
            // Получим параллельную проекцию
            arrProj = getParallelProjection(fallingObject[i]);
            // Нарисуем параллельную проекцию
            fillFigure(canvas_context, arrProj, figureColor);
        }

        // Если в видимой области еще есть кубики -
        // переходим на следующий шаг
        if (fallingObject.length > 0) {
            setTimeout(figureFalls, 100);
        }
        // иначе начинаем заново с новой фигурой
        else {
            setTimeout(figureStartsFalling, 100);
        }
    }

    // Получим центральную координату плоскости
    // для расчета удаленности от задней стенки
    function getSidecentralCoordinate(side) {

        var centralCoordinate = {x:0, y:0, z:0};

        for (var i = 0; i < 4; i++) {
            centralCoordinate.x += side[i].x;
            centralCoordinate.y += side[i].y;
            centralCoordinate.z += side[i].z;
        }

        centralCoordinate.x = centralCoordinate.x / 4;
        centralCoordinate.y = centralCoordinate.y / 4;
        centralCoordinate.z = centralCoordinate.z / 4;

        centralCoordinate.m = side[4].m;

        return centralCoordinate;
    }

    // Получим центральную координату кубика
    // для расчета удаленности от начала координат
    function getCubecentralCoordinate(side) {
        var centralCoordinate = {x:0, y:0, z:0};

        for (var i = 0; i < 6; i++) {
            centralCoordinate.x += side[i][4].x;
            centralCoordinate.y += side[i][4].y;
            centralCoordinate.z += side[i][4].z;
        }

        centralCoordinate.x = centralCoordinate.x / 6;
        centralCoordinate.y = centralCoordinate.y / 6;
        centralCoordinate.z = centralCoordinate.z / 6;

        return centralCoordinate;
    }

    // Поворачиваем точку t(x,y,z) на угол (deg) по оси (Y)
    // относительно точки t0(x,y,z)
    function rotateOnDegreeY(t0, t) {

        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;
    }

    // Поворачиваем точку t(x,y,z) на угол (deg) по оси (X)
    // относительно точки t0(x,y,z)
    function rotateOnDegreeX(t0, t) {

        var t_new = {};

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

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

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

    // Поворачиваем точку t(x,y,z) на угол (deg) по оси (Z)
    // относительно точки t0(x,y,z)
    function rotateOnDegreeZ(t0, t) {

        var t_new = {};

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

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

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

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

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

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

        var i, j, k;

        var arr_new = [];

        // Попарно работаем с гранями
        arr_new[0] = getSideParallelProjection(arr[0], arr[1], arr[6]); // Левая
и правая
        arr_new[1] = getSideParallelProjection(arr[2], arr[3], arr[6]); // Задняя
и передняя
        arr_new[2] = getSideParallelProjection(arr[4], arr[5], arr[6]); // Верхняя
и нижняя

        return arr_new;
    }

    // Параллельная проекция грани
    function getSideParallelProjection(side1, side2, cubeCenter) {

        // Сначала выясним какая пара нам попалась
        var centralProjection1 = getPointParallelProjection(side1[4]);
        var centralProjection2 = getPointParallelProjection(side2[4]);

        var coordinate = {
            x:Math.abs(side1[4].x - side2[4].x),
            y:Math.abs(side1[4].y - side2[4].y),
            z:Math.abs(side1[4].z - side2[4].z)};

        var i;

    // Найдем дальнюю (1) и ближнюю (2) от экрана точки
    // по оси (Z) и посчитаем их проекции
    var point1 = {z:10000},
        point2 = {z:-10000},
        point3 = {z:10000};

    for (i = 0; i < 4; i++) {
        if (point1.z > side1[i].z) {
            point1 = side1[i];
        }
        if (point1.z == side1[i].z
            && point1.x > side1[i].x) {
            point1 = side1[i];
        }
        if (point1.z == side1[i].z
            && point1.x == side1[i].x
            && point1.y < side1[i].y) {
            point1 = side1[i];
        }
        if (point2.z < side1[i].z) {
            point2 = side1[i];
        }
        if (point2.z == side1[i].z
            && point2.x < side1[i].x) {
            point2 = side1[i];
        }
        if (point2.z == side1[i].z
            && point2.x == side1[i].x
            && point2.y > side1[i].y) {
            point2 = side1[i];
        }
    }

    for (i = 0; i < 4; i++) {
        if (point3.z > side1[i].z
            && point1 != side1[i]) {
            point3 = side1[i];
        }
        if (point3.z == side1[i].z
            && point3.x > side1[i].x
            && point1 != side1[i]) {
            point3 = side1[i];
        }
        if (point3.z == side1[i].z
            && point3.x == side1[i].x
            && point3.y < side1[i].y
            && point1 != side1[i]) {
            point3 = side1[i];
        }
    }

	var direction = (point3.x - point1.x) / (point2.x - point1.x)
                            - (point3.y - point1.y) / (point2.y - point1.y);
        var projection = [];

        // (X) Правая и левая грани
        if (coordinate.x > coordinate.y && coordinate.x > coordinate.z) {

            point1 = getPointParallelProjection(point1);
            point2 = getPointParallelProjection(point2);
            point3 = getPointParallelProjection(point3);

            var direction = (point3.x - point1.x) / (point2.x - point1.x)
                - (point3.y - point1.y) / (point2.y - point1.y);

            var selected;

            if (point1.x >= point2.x) {
                if (centralProjection1.x >= centralProjection2.x) {
                    selected = side1;
                } else {
                    selected = side2;
                }
            } else {
                if (centralProjection1.x <= centralProjection2.x) {
                    selected = side1;
                } else {
                    selected = side2;
                }
            }

            if (selected.length < 6) {
                for (i = 0; i < 4; i++) {
                    projection[i] = getPointParallelProjection(selected[i]);
                }
                projection[4] = 'rgb(200,200,0)';
            }
        }

        // (Z) Передняя и задняя грани
        else if (coordinate.z > coordinate.x && coordinate.z > coordinate.y) {

            var selected;

            if (point1.x >= point2.x && direction < 0) {
                if (centralProjection1.x >= centralProjection2.x) {
                    selected = side1;
                } else {
                    selected = side2;
                }
            } else {
                if (centralProjection1.x <= centralProjection2.x) {
                    selected = side1;
                } else {
                    selected = side2;
                }
            }

            if (selected.length < 6) {
                for (i = 0; i < 4; i++) {
                    projection[i] = getPointParallelProjection(selected[i]);
                }
                projection[4] = 'rgb(200,0,0)';
            }
        }

        // (Y) Верхняя и нижняя грани
        else {

            var selected;

            if (point1.y <= point2.y) {
                if (centralProjection1.y <= centralProjection2.y) {
                    selected = side1;
                } else {
                    selected = side2;
                }
            } else {
                if (centralProjection1.y >= centralProjection2.y) {
                    selected = side1;
                } else {
                    selected = side2;
                }
            }

            if (selected.length < 6) {
                for (i = 0; i < 4; i++) {
                    projection[i] = getPointParallelProjection(selected[i]);
                }
                projection[4] = 'rgb(0,0,0)';
            }
        }

        return projection;
    }
    // Параллельная проекция точки
    function getPointParallelProjection(point) {
        return {
            x:point.x,
            y:point.y + point.z / 4};
//        return {
//            x:point.x + point.z * Math.cos(Math.PI * 3 / 4) / 2,
//            y:point.y + point.z * Math.sin(Math.PI * 3 / 4) / 2};
    }

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

        for (var i = 0; i < arr.length; i++) {
            context.beginPath();
            for (var j = 0; j < arr[i].length; j++) {
                if (arr[i][4] != undefined) {
                    context.fillStyle = arr[i][4];
                } else {
                    context.fillStyle = color;
                }
                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.fill();
            context.stroke();
        }
    }

    // Запускаем наш скрипт
    window.onload = function() {
        setTimeout(initAnimation, 200);
    };

    

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


Ответы

Ответ 1



Слова ничего не значат. Покажите мне код. (c) Линус Торвальдс Challenge accepted. Фиддл 'use strict'; var figures; var figure_111 = { //╔═══╗ //║ ║ //╚═══╝ figureType: 5, figurePositionY: 1, figurePositionX: 1, figurePositionZ: 1, shape: [ [[5]] ], rotateZ: function () { this.shape = rotateZ(this.shape); this.figurePositionZ = changePosition(this.figurePositionZ); } }; function initAnimation() { figures = [ figure_111 ]; // Запускаем падение figureStartsFalling(); } // Текущая фигура var currentFigure; // Размер фигуры var size = 70; // Объект на основании фигуры var fallingObject; // Цвет фигуры var figureColor; // Центр фигуры var t0; var canvas = document.getElementById('animation'); var canvas_context = canvas.getContext('2d'); // Сместим центр оси (Х) в середину поля canvas_context.translate(canvas.width / 2, 0); var doTenTimes; function figureStartsFalling() { doTenTimes = 30; // Переопределяем текущую фигуру currentFigure = figures[Math.floor(Math.random() * figures.length)]; // Получим цвет текущей фигуры figureColor = 'rgb(0,200,0)'; // Высота фигуры var figureHeight = currentFigure.shape.length; // Строим 3Д объект на основании фигуры fallingObject = []; var cube = []; var side = []; for (var y = currentFigure.shape.length - 1; y > -1; y--) { for (var x = 0; x < currentFigure.shape[0].length; x++) { for (var z = 0; z < currentFigure.shape[0][0].length; z++) { // Для каждой клеточки создаем кубик с 5 гранями cube = []; if (currentFigure.shape[y][x][z] != 0) { // Левая стенка side = [ {x:x*size, y:y*size - figureHeight*size, z:z*size}, {x:x*size, y:y*size - figureHeight*size, z:z*size + size}, {x:x*size, y:y*size + size - figureHeight*size, z:z*size + size}, {x:x*size, y:y*size + size - figureHeight*size, z:z*size}, {m:'left'} ]; // Центральная координата, нужна для расчета удаленности по осям (X) и (Z) side[4] = getSidecentralCoordinate(side); // Если левее чтото есть, то не рисуем if (x > 0 && currentFigure.shape[y][x - 1][z] != 0) { side[5] = false; } cube.push(side); // Правая стенка side = [ {x:x*size + size, y:y*size - figureHeight*size, z:z*size}, {x:x*size + size, y:y*size + size - figureHeight*size, z:z*size}, {x:x*size + size, y:y*size + size - figureHeight*size, z:z*size + size}, {x:x*size + size, y:y*size - figureHeight*size, z:z*size + size}, {m:'right'} ]; side[4] = getSidecentralCoordinate(side); // Если правее чтото есть, то не рисуем if (x + 1 < currentFigure.shape[0].length && currentFigure.shape[y][x + 1][z] != 0) { side[5] = false; } cube.push(side); // Задняя стенка side = [ {x:x*size, y:y*size - figureHeight*size, z:z*size}, {x:x*size, y:y*size + size - figureHeight*size, z:z*size}, {x:x*size + size, y:y*size + size - figureHeight*size, z:z*size}, {x:x*size + size, y:y*size - figureHeight*size, z:z*size}, {m:'rear'} ]; side[4] = getSidecentralCoordinate(side); // Если сзади чтото есть, то не рисуем if (z > 0 && currentFigure.shape[y][x][z - 1] != 0) { side[5] = false; } cube.push(side); // Передняя стенка side = [ {x:x*size, y:y*size - figureHeight*size, z:z*size + size}, {x:x*size + size, y:y*size - figureHeight*size, z:z*size + size}, {x:x*size + size, y:y*size + size - figureHeight*size, z:z*size + size}, {x:x*size, y:y*size + size - figureHeight*size, z:z*size + size}, {m:'front'} ]; side[4] = getSidecentralCoordinate(side); // Если впереди чтото есть, то не рисуем if (z + 1 < currentFigure.shape[0][0].length && currentFigure.shape[y][x][z + 1] != 0) { side[5] = false; } cube.push(side); // Верхняя стенка side = [ {x:x*size, y:y*size - figureHeight*size, z:z*size}, {x:x*size + size, y:y*size - figureHeight*size, z:z*size}, {x:x*size + size, y:y*size - figureHeight*size, z:z*size + size}, {x:x*size, y:y*size - figureHeight*size, z:z*size + size}, {m:'top'} ]; side[4] = getSidecentralCoordinate(side); // Если сверху чтото есть, то не рисуем if (y > 0 && currentFigure.shape[y - 1][x][z] != 0) { side[5] = false; } cube.push(side); // Нижняя стенка side = [ {x:x*size, y:y*size + size - figureHeight*size, z:z*size}, {x:x*size, y:y*size + size - figureHeight*size, z:z*size + size}, {x:x*size + size, y:y*size + size - figureHeight*size, z:z*size + size}, {x:x*size + size, y:y*size + size - figureHeight*size, z:z*size}, {m:'bottom'} ]; side[4] = getSidecentralCoordinate(side); // Если снизу чтото есть, то не рисуем if (y + 1 < currentFigure.shape.length && currentFigure.shape[y + 1][x][z] != 0) { side[5] = false; } cube.push(side); fallingObject.push(cube); } } } } // Определим центр фигуры t0 = { y:currentFigure.shape.length / 2 * size - figureHeight*size, x:currentFigure.shape[0].length / 2 * size, z:currentFigure.shape[0][0].length / 2 * size }; // Запускаем падение setTimeout(figureFalls, 100); } // Угол поворота, не изменяется var deg = 1; // Текущая проекция var arrProj; var tempArr; function figureFalls() { // Сначала очистим экран - закрасим белым цветом clearScreen(canvas, canvas_context, 'rgb(255,255,255)'); // Изменяем положение текущей фигуры for (var i = 0; i < fallingObject.length; i++) { for (var j = 0; j < 6; j++) { for (var k = 0; k < 4; k++) { // Повернем фигуру на угол fallingObject[i][j][k] = rotateOnDegreeY(t0, fallingObject[i][j][k]); fallingObject[i][j][k] = rotateOnDegreeX(t0, fallingObject[i][j][k]); fallingObject[i][j][k] = rotateOnDegreeZ(t0, fallingObject[i][j][k]); // и опустим вниз на 1px fallingObject[i][j][k].y += 1; } // Пересчитаем центральную координату плоскости fallingObject[i][j][4] = getSidecentralCoordinate(fallingObject[i][j]); } doTenTimes -= 1; // Пересчитаем центральную координату кубика fallingObject[i][6] = getCubecentralCoordinate(fallingObject[i]); // Если кубик ушел слишком далеко - удаляем кубик if (fallingObject[i][6].y > canvas.height) { fallingObject.splice(i, 1); i -=1; } } // Центр фигуры тоже опускается t0.y += 1; // Сортируем кубики по удаленности от начала координат по (X) и (Z) var exit = false; while (!exit) { exit = true; for (var i = 0; i < fallingObject.length - 1; i++) { if (fallingObject[i][6].z > fallingObject[i + 1][6].z) { tempArr = fallingObject[i]; fallingObject[i] = fallingObject[i + 1]; fallingObject[i + 1] = tempArr; exit = false; } else if (fallingObject[i][6].z == fallingObject[i + 1][6].z) { if (fallingObject[i][6].x > fallingObject[i + 1][6].x) { tempArr = fallingObject[i]; fallingObject[i] = fallingObject[i + 1]; fallingObject[i + 1] = tempArr; exit = false; } } } } // Отрисовываем фигуру for (var i = 0; i < fallingObject.length; i++) { // Получим параллельную проекцию arrProj = getParallelProjection(fallingObject[i]); // Нарисуем параллельную проекцию fillFigure(canvas_context, arrProj, figureColor); } // Если в видимой области еще есть кубики - // переходим на следующий шаг if (fallingObject.length > 0) { setTimeout(figureFalls, 100); } // иначе начинаем заново с новой фигурой else { setTimeout(figureStartsFalling, 100); } } // Получим центральную координату плоскости // для расчета удаленности от задней стенки function getSidecentralCoordinate(side) { var centralCoordinate = {x:0, y:0, z:0}; for (var i = 0; i < 4; i++) { centralCoordinate.x += side[i].x; centralCoordinate.y += side[i].y; centralCoordinate.z += side[i].z; } centralCoordinate.x = centralCoordinate.x / 4; centralCoordinate.y = centralCoordinate.y / 4; centralCoordinate.z = centralCoordinate.z / 4; centralCoordinate.m = side[4].m; return centralCoordinate; } // Получим центральную координату кубика // для расчета удаленности от начала координат function getCubecentralCoordinate(side) { var centralCoordinate = {x:0, y:0, z:0}; for (var i = 0; i < 6; i++) { centralCoordinate.x += side[i][4].x; centralCoordinate.y += side[i][4].y; centralCoordinate.z += side[i][4].z; } centralCoordinate.x = centralCoordinate.x / 6; centralCoordinate.y = centralCoordinate.y / 6; centralCoordinate.z = centralCoordinate.z / 6; return centralCoordinate; } // Поворачиваем точку t(x,y,z) на угол (deg) по оси (Y) // относительно точки t0(x,y,z) function rotateOnDegreeY(t0, t) { 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; } // Поворачиваем точку t(x,y,z) на угол (deg) по оси (X) // относительно точки t0(x,y,z) function rotateOnDegreeX(t0, t) { var t_new = {}; // Переводим угол поворота из градусов в радианы var rad = (Math.PI / 180) * deg; // Рассчитываем координаты новой точки по формуле t_new.x = t.x; t_new.y = t0.y + (t.y - t0.y) * Math.cos(rad) - (t.z - t0.z) * Math.sin(rad); t_new.z = t0.z + (t.y - t0.y) * Math.sin(rad) + (t.z - t0.z) * Math.cos(rad); // Возвращаем полученное значение return t_new; } // Поворачиваем точку t(x,y,z) на угол (deg) по оси (Z) // относительно точки t0(x,y,z) function rotateOnDegreeZ(t0, t) { var t_new = {}; // Переводим угол поворота из градусов в радианы var rad = (Math.PI / 180) * deg; // Рассчитываем координаты новой точки по формуле t_new.x = t0.x + (t.x - t0.x) * Math.cos(rad) - (t.y - t0.y) * Math.sin(rad); t_new.y = t0.y + (t.x - t0.x) * Math.sin(rad) + (t.y - t0.y) * Math.cos(rad); t_new.z = t.z; // Возвращаем полученное значение return t_new; } // Закрашиваем весь экран определенным цветом function clearScreen(canvas, context, color) { context.fillStyle = color; context.beginPath(); context.fillRect(- canvas.width / 2, 0, canvas.width, canvas.height); context.closePath(); context.fill(); } // Получаем параллельную проекцию кубика на плоскость экрана function getParallelProjection(arr) { var i, j, k; var projected = []; var projection; for (i = 0; i < 6; i++) { projection = []; if (arr[i].length < 6) { for (j = 0; j < 4; j++) { projection[j] = getPointParallelProjection(arr[i][j]); } } projected[i] = projection; } return projected.filter(isClockwise); } function isClockwise(point_array) { if (point_array.length < 3) return false; var p0 = point_array[0]; var p1 = point_array[1]; var p2 = point_array[2]; return (p1.x-p0.x)*(p2.y-p0.y) - (p2.x-p0.x)*(p1.y-p0.y) > 0; } // Параллельная проекция точки function getPointParallelProjection(point) { return { x:point.x, y:point.y + point.z / 4}; } // Рисуем фигуру по точкам из массива function fillFigure(context, arr, color) { context.lineWidth = 2; context.strokeStyle = 'rgb(255,255,255)'; context.fillStyle = color; for (var i = 0; i < arr.length; i++) { context.beginPath(); for (var j = 0; j < arr[i].length; j++) { if (arr[i][4] != undefined) { context.fillStyle = arr[i][4]; } else { context.fillStyle = color; } 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.fill(); context.stroke(); } context.font = '12px'; context.fillText('Рисуется сторон: ' + arr.length, -120, 20); } // Запускаем наш скрипт window.onload = function() { setTimeout(initAnimation, 200); };

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

Релевантный кусок: // Получаем параллельную проекцию кубика на плоскость экрана function getParallelProjection(arr) { var i, j, k; var projected = []; var projection; for (i = 0; i < 6; i++) { projection = []; if (arr[i].length < 6) { for (j = 0; j < 4; j++) { projection[j] = getPointParallelProjection(arr[i][j]); } } projected[i] = projection; } return projected.filter(isClockwise); } function isClockwise(point_array) { if (point_array.length < 3) return false; var p0 = point_array[0]; var p1 = point_array[1]; var p2 = point_array[2]; return (p1.x-p0.x)*(p2.y-p0.y) - (p2.x-p0.x)*(p1.y-p0.y) > 0; } Вся магия в том, что мы проверяем в том, как рисуются грани. Для этого, правда, пришлось поправить порядок задания вершин в самом начале, дабы каждая грань рисовалась по часовой стрелке. После проекции "векторным" произведением проверяется порядок спроецированных вершин, и если он оказался против часовой стрелки - этот набор вершин отбрасывается. Бонусом на канвас выводится число граней, переданных в функцию отрисовки - чисто так, для верности расчётов. Стоит ли говорить, что решена несколько иная задача, чем поставленная в вопросе, но код есть код :)

Ответ 2



Второй способ: подсказано пользователем Дмитрий Чистик. Берем ближнюю вершину и отображаем 3 отходящих от нее грани. Лагов меньше, чем в первом случае, но все равно они есть. Такой код тоже не работает. 'use strict'; // Массив с фигурами var figures; var figure_111 = { //╔═══╗ //║ ║ //╚═══╝ figureType: 5, figurePositionY: 1, figurePositionX: 1, figurePositionZ: 1, shape: [ [[5]] ], rotateZ: function () { this.shape = rotateZ(this.shape); this.figurePositionZ = changePosition(this.figurePositionZ); } }; // Инициализация нашего скрипта // эту функцию надо будет вставить // в переменную window.onload function initAnimation() { figures = [ figure_111 ]; // Запускаем падение figureStartsFalling(); } // Текущая фигура var currentFigure; // Размер фигуры var size = 30; // Объект на основании фигуры var fallingObject; // Цвет фигуры var figureColor; // Центр фигуры var t0; var canvas = document.getElementById('animation'); var canvas_context = canvas.getContext('2d'); // Сместим центр оси (Х) в середину поля canvas_context.translate(canvas.width / 2, 0); var doTenTimes; function figureStartsFalling() { doTenTimes = 30; // Переопределяем текущую фигуру currentFigure = figures[Math.floor(Math.random() * figures.length)]; // Получим цвет текущей фигуры figureColor = 'rgb(0,200,0)'; // Высота фигуры var figureHeight = currentFigure.shape.length; // Строим 3Д объект на основании фигуры fallingObject = []; var cube = []; var side = []; for (var y = currentFigure.shape.length - 1; y > -1; y--) { for (var x = 0; x < currentFigure.shape[0].length; x++) { for (var z = 0; z < currentFigure.shape[0][0].length; z++) { // Для каждой клеточки создаем кубик с 5 гранями cube = []; if (currentFigure.shape[y][x][z] != 0) { // Левая стенка side = [ {x:x*size, y:y*size - figureHeight*size, z:z*size}, {x:x*size, y:y*size - figureHeight*size, z:z*size + size}, {x:x*size, y:y*size + size - figureHeight*size, z:z*size + size}, {x:x*size, y:y*size + size - figureHeight*size, z:z*size}, {m:'left'} ]; // Центральная координата, нужна для расчета удаленности по осям (X) и (Z) side[4] = getSidecentralCoordinate(side); // Если левее чтото есть, то не рисуем if (x > 0 && currentFigure.shape[y][x - 1][z] != 0) { side[5] = false; } cube.push(side); // Правая стенка side = [ {x:x*size + size, y:y*size - figureHeight*size, z:z*size}, {x:x*size + size, y:y*size - figureHeight*size, z:z*size + size}, {x:x*size + size, y:y*size + size - figureHeight*size, z:z*size + size}, {x:x*size + size, y:y*size + size - figureHeight*size, z:z*size}, {m:'right'} ]; side[4] = getSidecentralCoordinate(side); // Если правее чтото есть, то не рисуем if (x + 1 < currentFigure.shape[0].length && currentFigure.shape[y][x + 1][z] != 0) { side[5] = false; } cube.push(side); // Задняя стенка side = [ {x:x*size, y:y*size - figureHeight*size, z:z*size}, {x:x*size + size, y:y*size - figureHeight*size, z:z*size}, {x:x*size + size, y:y*size + size - figureHeight*size, z:z*size}, {x:x*size, y:y*size + size - figureHeight*size, z:z*size}, {m:'rear'} ]; side[4] = getSidecentralCoordinate(side); // Если сзади чтото есть, то не рисуем if (z > 0 && currentFigure.shape[y][x][z - 1] != 0) { side[5] = false; } cube.push(side); // Передняя стенка side = [ {x:x*size, y:y*size - figureHeight*size, z:z*size + size}, {x:x*size + size, y:y*size - figureHeight*size, z:z*size + size}, {x:x*size + size, y:y*size + size - figureHeight*size, z:z*size + size}, {x:x*size, y:y*size + size - figureHeight*size, z:z*size + size}, {m:'front'} ]; side[4] = getSidecentralCoordinate(side); // Если впереди чтото есть, то не рисуем if (z + 1 < currentFigure.shape[0][0].length && currentFigure.shape[y][x][z + 1] != 0) { side[5] = false; } cube.push(side); // Верхняя стенка side = [ {x:x*size, y:y*size - figureHeight*size, z:z*size}, {x:x*size, y:y*size - figureHeight*size, z:z*size + size}, {x:x*size + size, y:y*size - figureHeight*size, z:z*size + size}, {x:x*size + size, y:y*size - figureHeight*size, z:z*size}, {m:'top'} ]; side[4] = getSidecentralCoordinate(side); // Если сверху чтото есть, то не рисуем if (y > 0 && currentFigure.shape[y - 1][x][z] != 0) { side[5] = false; } cube.push(side); // Нижняя стенка side = [ {x:x*size, y:y*size + size - figureHeight*size, z:z*size}, {x:x*size, y:y*size + size - figureHeight*size, z:z*size + size}, {x:x*size + size, y:y*size + size - figureHeight*size, z:z*size + size}, {x:x*size + size, y:y*size + size - figureHeight*size, z:z*size}, {m:'bottom'} ]; side[4] = getSidecentralCoordinate(side); // Если снизу чтото есть, то не рисуем if (y + 1 < currentFigure.shape.length && currentFigure.shape[y + 1][x][z] != 0) { side[5] = false; } cube.push(side); fallingObject.push(cube); } } } } // Определим центр фигуры t0 = { y:currentFigure.shape.length / 2 * size - figureHeight*size, x:currentFigure.shape[0].length / 2 * size, z:currentFigure.shape[0][0].length / 2 * size }; // Запускаем падение setTimeout(figureFalls, 100); } // Угол поворота, не изменяется var deg = 1; // Текущая проекция var arrProj; var tempArr; function figureFalls() { // Сначала очистим экран - закрасим белым цветом clearScreen(canvas, canvas_context, 'rgb(255,255,255)'); // Изменяем положение текущей фигуры for (var i = 0; i < fallingObject.length; i++) { for (var j = 0; j < 6; j++) { for (var k = 0; k < 4; k++) { // Повернем фигуру на угол fallingObject[i][j][k] = rotateOnDegreeY(t0, fallingObject[i][j][k]); fallingObject[i][j][k] = rotateOnDegreeX(t0, fallingObject[i][j][k]); fallingObject[i][j][k] = rotateOnDegreeZ(t0, fallingObject[i][j][k]); // и опустим вниз на 1px fallingObject[i][j][k].y += 1; } // Пересчитаем центральную координату плоскости fallingObject[i][j][4] = getSidecentralCoordinate(fallingObject[i][j]); } doTenTimes -= 1; // Пересчитаем центральную координату кубика fallingObject[i][6] = getCubecentralCoordinate(fallingObject[i]); // Если кубик ушел слишком далеко - удаляем кубик if (fallingObject[i][6].y > canvas.height) { fallingObject.splice(i, 1); i -=1; } } // Центр фигуры тоже опускается t0.y += 1; // Сортируем кубики по удаленности от начала координат по (X) и (Z) var exit = false; while (!exit) { exit = true; for (var i = 0; i < fallingObject.length - 1; i++) { if (fallingObject[i][6].z > fallingObject[i + 1][6].z) { tempArr = fallingObject[i]; fallingObject[i] = fallingObject[i + 1]; fallingObject[i + 1] = tempArr; exit = false; } else if (fallingObject[i][6].z == fallingObject[i + 1][6].z) { if (fallingObject[i][6].x > fallingObject[i + 1][6].x) { tempArr = fallingObject[i]; fallingObject[i] = fallingObject[i + 1]; fallingObject[i + 1] = tempArr; exit = false; } } } } // Отрисовываем фигуру for (var i = 0; i < fallingObject.length; i++) { // Получим параллельную проекцию arrProj = getParallelProjection(fallingObject[i]); // Нарисуем параллельную проекцию fillFigure(canvas_context, arrProj, figureColor); } // Если в видимой области еще есть кубики - // переходим на следующий шаг if (fallingObject.length > 0) { setTimeout(figureFalls, 100); } // иначе начинаем заново с новой фигурой else { setTimeout(figureStartsFalling, 100); } } // Получим центральную координату плоскости // для расчета удаленности от задней стенки function getSidecentralCoordinate(side) { var centralCoordinate = {x:0, y:0, z:0}; for (var i = 0; i < 4; i++) { centralCoordinate.x += side[i].x; centralCoordinate.y += side[i].y; centralCoordinate.z += side[i].z; } centralCoordinate.x = centralCoordinate.x / 4; centralCoordinate.y = centralCoordinate.y / 4; centralCoordinate.z = centralCoordinate.z / 4; centralCoordinate.m = side[4].m; return centralCoordinate; } // Получим центральную координату кубика // для расчета удаленности от начала координат function getCubecentralCoordinate(side) { var centralCoordinate = {x:0, y:0, z:0}; for (var i = 0; i < 6; i++) { centralCoordinate.x += side[i][4].x; centralCoordinate.y += side[i][4].y; centralCoordinate.z += side[i][4].z; } centralCoordinate.x = centralCoordinate.x / 6; centralCoordinate.y = centralCoordinate.y / 6; centralCoordinate.z = centralCoordinate.z / 6; return centralCoordinate; } // Поворачиваем точку t(x,y,z) на угол (deg) по оси (Y) // относительно точки t0(x,y,z) function rotateOnDegreeY(t0, t) { 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; } // Поворачиваем точку t(x,y,z) на угол (deg) по оси (X) // относительно точки t0(x,y,z) function rotateOnDegreeX(t0, t) { var t_new = {}; // Переводим угол поворота из градусов в радианы var rad = (Math.PI / 180) * deg; // Рассчитываем координаты новой точки по формуле t_new.x = t.x; t_new.y = t0.y + (t.y - t0.y) * Math.cos(rad) - (t.z - t0.z) * Math.sin(rad); t_new.z = t0.z + (t.y - t0.y) * Math.sin(rad) + (t.z - t0.z) * Math.cos(rad); // Возвращаем полученное значение return t_new; } // Поворачиваем точку t(x,y,z) на угол (deg) по оси (Z) // относительно точки t0(x,y,z) function rotateOnDegreeZ(t0, t) { var t_new = {}; // Переводим угол поворота из градусов в радианы var rad = (Math.PI / 180) * deg; // Рассчитываем координаты новой точки по формуле t_new.x = t0.x + (t.x - t0.x) * Math.cos(rad) - (t.y - t0.y) * Math.sin(rad); t_new.y = t0.y + (t.x - t0.x) * Math.sin(rad) + (t.y - t0.y) * Math.cos(rad); t_new.z = t.z; // Возвращаем полученное значение return t_new; } // Закрашиваем весь экран определенным цветом function clearScreen(canvas, context, color) { context.fillStyle = color; context.beginPath(); context.fillRect(- canvas.width / 2, 0, canvas.width, canvas.height); context.closePath(); context.fill(); } // Получаем параллельную проекцию кубика на плоскость экрана function getParallelProjection(arr) { var i, j, k; var arr_new = []; // Найдем ближнюю по оси (Z) вершину var point = {x:-10000, y:10000, z:-10000}; for (i = 0; i < 6; i++) { for (j = 0; j < 4; j++) { if (arr[i][j].z > point.z) { point = arr[i][j]; } if (arr[i][j].z == point.z && arr[i][j].x > point.x) { point = arr[i][j]; } if (arr[i][j].z == point.z && arr[i][j].x == point.x && arr[i][j].y < point.y) { point = arr[i][j]; } } } // Упорядочим грани по удаленности от ближней вершины var exit, temp; do { exit = true; for (i = 0; i < 5; i++) { if (findDistance(point, arr[i][4]) > findDistance(point, arr[i + 1][4])) { temp = arr[i]; arr[i] = arr[i + 1]; arr[i + 1] = temp; exit = false; } } } while (!exit); var projection; for (i = 0; i < 3; i++) { projection = []; if (arr[i].length < 6) { for (j = 0; j < 4; j++) { projection[j] = getPointParallelProjection(arr[i][j]); } } arr_new[i] = projection; } return arr_new; } // Находим расстояние между двумя точками в пространстве function findDistance(point1, point2) { return Math.sqrt( Math.pow((point2.x - point1.x), 2) + Math.pow((point2.y - point1.y), 2) + Math.pow((point2.z - point1.z), 2) ) } // Параллельная проекция точки function getPointParallelProjection(point) { return { x:point.x, y:point.y + point.z / 4}; } // Рисуем фигуру по точкам из массива function fillFigure(context, arr, color) { context.lineWidth = 2; context.strokeStyle = 'rgb(255,255,255)'; context.fillStyle = color; for (var i = 0; i < arr.length; i++) { context.beginPath(); for (var j = 0; j < arr[i].length; j++) { if (arr[i][4] != undefined) { context.fillStyle = arr[i][4]; } else { context.fillStyle = color; } 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.fill(); context.stroke(); } } // Запускаем наш скрипт window.onload = function() { setTimeout(initAnimation, 200); };

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



Ответ 3



В чем собственно сложность? Преобразуете все вершины Вычисляете центры граней Берете три ближних Или даже еще проще: Берете центры граней Преобразуете центры граней согласно проекции Берете три ближних

понедельник, 6 января 2020 г.

Вращение текста вокруг оси Y

#css #html5 #css3 #svg #3d


В последние несколько дней я немного занимался свойством CSS transform, и я хочу
найти способ для отображения переспективы, похожее на это:  

   

С Boxes - все обычные прямоугольные Boxes. Я просто не знаю с чего начать.     

Мой код:    



body {
  margin: 0px;
  margin-left: 15%;
  overflow: hidden;
}
.skewed {
  margin: -1px;
  height: 300px;
  border: 4px solid black;
  width: 70%;
  float: left;
  overflow: hidden;
  transform: perspective(200px) rotateY(45deg);
}
.skewed > img {
  filter: url("data:image/svg+xml;utf8,#grayscale")
blur(3px);
  filter: gray;
  /* IE6-9 */
  -webkit-filter: grayscale(100%) blur(3px);
  /* Chrome 19+ & Safari 6+ */
  transition: 0.2s ease-in-out;
}
.skewed:hover > img {
  transform: scale(1.05);
  filter: none;
  -webkit-filter: grayscale(0%);
}
Источник: Rotate text on th Y axis


Ответы

Ответ 1



Это довольно просто с CSS 3D-transforms. Вы можете вращать каждый элемент по оси Y с помощью Transform:rotateY(xdeg); Здесь пример: div { font-size: 2em; width: 7em; margin: 0 auto; padding: 0.1em 0; text-align: center; transform: perspective(500px) rotateY(30deg); background: teal; } div:nth-child(2n) { transform: perspective(500px) rotateY(-30deg); background: tomato; }
some text
some text
some text
some text
Обратите внимание, что вам нужно будет добавить префиксы для поддержки браузера. Смотрите canIuse для получения дополнительной информации Источник ответа: @web-tiki

воскресенье, 5 января 2020 г.

Конвертирование 3D сцены в 2D сцену

#wpf #3d #xaml #c_sharp


Ситуация следующая: имеется достаточно большая 3D модель системы, полученная конвертацией
из 3DMax (размер xaml разметки больше 50мб). Для работы с моделью используется Viewport3D.
Но для отображения нужен лишь фронтальный вид. Основной проблемой становится ресурсоемкость
получающегося приложения.
Вопрос: существует ли способ для приведения 3D сцены к 2D виду? Желательно без существенной
потери качества.    


Ответы

Ответ 1



То что вы хотите нельзя сделать, кроме как превратив в статическое изображение. Рендеринг 3D-сцены - это и есть приведение к 2D. Единственное, что можно было бы сделать, это всё превратить в какой-нибудь флеш, который в принципе работает именно с 2D, но тогда все объекты по отдельности придётся как-то растеризовать или превращать в плоские векторные объекты. И не факт что это будет работать быстрее, или что это будет меньше весить.

Ответ 2



XAML файлы - это некомпилированные Baml. Может стоит создать алгоритм их хранения в бинарном виде вроде отдельного файла, который можно прочитать и отправить в Viewport3D. Еще скорее всего если детально просмотреть сохраненный результат из 3DMax`a, скорее всего он будет просто ужасным. Здесь тоже можно кое-что выкинуть. В любом случае нужно поработать ручками.

суббота, 4 января 2020 г.

Алгоритм определения коллизии цилиндра и сложного тела

#алгоритм #3d


Дано:

1) Сложное тело (меш ландшафта), состоящее из треугольных полигонов

2) Простое тело (к примеру цилиндр). Для него известна точка центра и все его измерения.

Нужен алгоритм определения наличия пересечения этих двух тел. При этом, сложное тело
много больше простого (по размерам и количеству вершин).

Основная задача - как можно быстрее ответить, есть ли пересечение между этими телами.
Допускается предобработка сложного тела.

Какие вообще есть подходы для решения подобной задачи?



Так же скажу сразу, что нужен именно алгоритм, а не какой-то физический движок, который
все это сам определит.
    


Ответы

Ответ 1



Разбить поиск на этапы. Первый это грубое отсечение, по крайним координатам тел. Если коллизия не найдена, значит тела не пересекаются. Если найдена, тогда преступаем к детальному отсечению по полигонам попавшим в эту область. Можете почитать более детально про алгоритмы Sweep And Prune и V-Clip

Ответ 2



Если хотите быстро - надо математически: По сути, необходимо проверить на пересечение каждого примитива из которого состоит сложное тело с цилиндром. Допустим известно уравнение поверхности какой-то части сложного тела, тогда наличие решения системы из двух уравнений, в котором первое - уравнение этой поверхности, а второе - параметрическое(либо ещё какое-то) уравнение цилиндра, даст ответ пересекаются ли они (тоесть имеют ли общие точки). Если с хотя бы одной частью есть пересечение алгоритм останавливается, иначе нету пересечений.

четверг, 2 января 2020 г.

Какие практичные методики оптимизации кода OpenGl 3D движков кроме перечисленных ниже вы знаете?

#разработка_игр #3d #оптимизация #opengl



Вертексные буферные объекты.
Отрисовка треугольников одного материала для снижения количества переключений между
текстурами, цветом и компилированными шейдерами.
Удаление из кадра ненужных объектов. 
Рефакторинг кода стандартными методами.
Перенос матричных операций с мировыми координатами объектов в шейдеры.
Рендер спецэффектов в другой программе и отрисовка их из готового файла. (Используется
больше памяти, но быстрее так как не нужно проводить предварительные расчеты для отрисовки
спецэффекта.)
Использование diffuse карт для дешевого выбивания полигонов из модели без потери
качества.
Mipmapping.
Использование где это возможно заранее заготовленных карт освещения вместо real-time
расчетов
Использование triangle strips для экономии памяти.
Использование большего числа текстур текстур с размерами кратными степени двойки.
Перенос real-time расчета освещения в шейдеры.
Исключения из тела главной функции, которая отрисовывает кадр, всех сложных математических
операций, замена их заранее обсчитанными статичными объектами.
Перенос кода для детектирования столкновений, AI и других функций не связанных напрямую
с отрисовкой картинки в отдельный поток выполнения вызываемый с меньшей частотой чем FPS
Представление атрибутов вертексов в виде массива структур, а не структуры массивов(x1,y1,z1,x2,y2,z2,x3,y3,z3
вместо x1,x2,x3,y1,y2,y3,z1,z2,z3)

Под 3D движком понимается программа обертка над OpenGl, которая подготавливает массив
аттрибутов вертексов для подачи в трехмерный конвейер, естественно не поддерживающая
программный рендеринг при котором сам конвейер запрограммирован внутри кода движка.
Под оптимизацией понимается оптимизация по многим параметрам, быстродействию, качеству
картинки, экономии памяти, читабельности кода и.т.д
Интересуют только реальные методы легко и быстро реализуемые на практике прикладным
программистом.
Методы из советского ВТУЗовского курса вычислительной математики и численных методов,
а также алгоритмические-компиляторные методы вроде замены деления на два битовым сдвигом
повсюду внутри кода просьба не предлагать.    


Ответы

Ответ 1



Навскидку: Атласы - замена мелких текстур одной большой (что-то похожее видел в Вашем посте, но недопонял, о чём речь там). Occlusion query - отрисовываем простейшую модель вместо сложной, спрашиваем у видеокарты, сильно ли она перекроется, и узнаём, надо ли её рисовать. Использование временной когерентности для того же отбрасывания невидимых объектов. Instancing. Вообще говоря, Вы мешаете в кучу и графику, и игровую логику, так что конкретный ответ дать сложно. Более того, некоторые техники взаимоисключающие. Оптимизации применяются тогда, когда возникают узкие места, иначе толку от них нет. Например, если Вы упёрлись в fillrate, никакое применение VBO толку не даст, и наоборот, если основная проблема в диком количестве геометрии - можно не заморачиваться с атласами, оптимизацией шейдеров и тд,а сосредоточиться на отбрасывании невидимых треугольников и LOD. В общем случае, на неполностью загруженную видеокарту пытаются переложить все распараллеливающиеся задачи, а не только графику, в то время как процессору обычно отдают игровую логику(если видеокарта занята).

Ответ 2



к общему списку можно добавить использование лайтмапов для статичных объектов - и выглядит качественней, и ресурсы экономит

вторник, 24 декабря 2019 г.

JavaFX 8 3D : провести Cylinder от точки А до точки B, заданных Point3D объектами

#java #javafx #3d


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

Point3D pointA = new Point3D(100,200,150);
Point3D pointB = new Point3D(200,150,-100);


Есть группа (javafx.scene.Group), в которой производятся построения:

Group space3D = new Group();


Задача: соединить эти две виртуальные точки (можно разместить в этих координатах
по экземпляру Sphere радиусом 10, для наглядности) экземпляром javafx.scene.shape.Cylinder
радиусом 2, высотой равной расстоянию от точки А до точки B с минимальным количеством
действий.

П.С.: Какие альтернативы были бы более эффективными для визуального соединения двух
точек в 3D пространстве?
    


Ответы

Ответ 1



Начну от обратного.Пример решения: public Cylinder paintCylinder(Point3D A, Point3D B) { Point3D temp = A.subtract(B); double Y = temp.getX() != 0 || temp.getZ() != 0 ? B.getY() : B.getY() > A.getY() ? B.getY() : A.getY(); Point3D dir = A.subtract(B).crossProduct(new Point3D(0, -1, 0)); double angle = Math.acos(A.subtract(B).normalize().dotProduct(new Point3D(0, -1, 0))); double h1 = A.distance(B); Cylinder c = new Cylinder(2d, h1); c.getTransforms().addAll(new Translate(B.getX(), Y - h1 / 2d, B.getZ()), new Rotate(-Math.toDegrees(angle), 0d, h1 / 2d, 0d, new Point3D(dir.getX(), -dir.getY(), dir.getZ()))); return c; } Как результат: : теперь ближе к конкретным вопросам: Так сложилось что легче для понимания процедуры вращения объекта это последовательное вращение по 3 координатам (и применяется такой подход часто) Но если разобраться то вполне естественный ход поворачивать объект всего один раз на один конкретный угол.И если быть совсем точным поворачивают систему координат дабы на одной плоскости оказались точки начала и конца дуги поворота. Возможна также работа с матрицей поворота и как пример работы с ней данный ответ.Да конечно такой подход быстрее чем работа с последовательным поворотом по трем осям но хочу заметить что сам автор ответа(он же к слову и разработчик библиотеки - FXyz) по моим наблюдениям конкретно матричной реализацией не пользуется (наблюдение рамках открытого кода на GitHub).

воскресенье, 15 декабря 2019 г.

Равномерное распределение точек на сфере

#математика #алгоритм #3d


Ищу алгоритм для "красивого" заполнения поверхности сферы однородными объектами и
представляю себе входные условия на него таким образом:

Необходимо размещать заданное количество точек на сфере как можно более равномерно.
Под равномерно здесь понимается примерно вот что: для любой точки ее расстояние по
сфере до ближайшей соседней как можно более велико (назовем такое расстояние ореолом
точки).
Некоторое количество точек задано сразу, точки могут добавляться и удаляться. При
перераспределении точек они должны сдвигаться, но не прыгать, то есть меняющие свое
положение точки должны двигаться по гладким путям. Желательно, чтобы при движении точек
по путям их ореол либо только увеличивался, либо только уменьшался (точки либо все
вместе уплотняются при добавлении новых, либо наоборот)
Неплохо бы продумать модификации алгоритма для эллипсоида или для неоднородных объектов
(скажем, кругов различного радиуса - ореолом тогда будет наименьшее расстояние между
границами)

Ясно, что здесь при позиционировании предельная точность не так важна, как скорость
поиска "красивого" распределения. Очень желаю услышать полезные идеи по реализации
таких условий, а также возможные варианты других критериев "красивого" распределения.    


Ответы

Ответ 1



Пусть у каждой точки есть три степени свободы: x, y и z. На каждую точку действуют две силы: Отталкивание от других точек. Притяжение к поверхности сферы. Проецируя точки на сферу, получает распределение. При добавлении точки она будет "расталкивать" соседей. Пример для двумерного случая: Пример для трёхмерного случая:

Ответ 2



Метод золотого сечения (это Matlab, но думаю понятно будет) num_pts = 200; % количество точек indices = 0:num_pts + 0.5; theta = acos(1 - 2 * indices / num_pts); phi = pi * (1 + 5^0.5) * indices; r = 3; x = r .* cos(phi) .* sin(theta); y = r .* sin(phi) .* sin(theta); z = r .* cos(theta); Источник - https://stackoverflow.com/questions/9600801/evenly-distributing-n-points-on-a-sphere

вторник, 10 декабря 2019 г.

Как вычислить равномерное увеличение сложной 3D фигуры

#3d #геометрия #объекты #threejs #формулы


Добрый день, есть некий абстрактный 3D объект сложной геометрии.
Скажем его объем V₁ кубических метра. 

Задача расчитать величину (на иллюстрации выделена желтым) на которую надо сместить
каждую вершину объекта, чтобы в результате получился объект с новым объемом V₂.

Есть ли какая-нибудь формула для нахождения этой величины? 
В английском сегменте данная операция называется offset.


    


Ответы

Ответ 1



Disclaimer: с three.js не работал. Для каждой вершины новое положение: (kx, ky, kz). где k = кубический_корень_из(V2/V1) То есть координаты каждой вершины надо изменить на ((k - 1)x, (k - 1)y, (k - 1)z) А потом сдвиньте все координаты, чтобы, например, центры тяжести старой и новой фигуры совпадали, или описывающий параллелепипед равномерно расширился/сжался в направлениях координатных осей. Но это уже зависит от дополнительных условий задачи, не включенных в вопрос. Такое не должно пропасть. Сохраним для истории. Если для 2D такая же формула, но только с квадратным корнем, то она не работает. Если конечно правильно считал. Тот же прямоугольник 10x20. Площадь его 200 единиц. Хотим увеличить до 300. корень квадратный (300 / 200 ) = 1.224745... Если его применяем к сторонам, то у полученной фигуры площадь 279.5 Для 10х20 до 300, величина должна быть что-то около 1.51 А разве корень квадратный из 1.5 (300/200) не 1.224745? Остальное я не считал, а строил в Rhinoceros3D.

понедельник, 9 декабря 2019 г.

Простой способ визуализации в СИ

#c #графика #3d


Каким образом можно визуализировать результат работы программы, вычисляющей и возвращающей
координаты точек в трехмерном пространстве? Данные такого вида:

Xn,Yn,Zn


,где n - координаты.
В общем мне нужно визуализировать геометрические фигуры, состоящие из массива точек
и проверить правильность расчетов.
    


Ответы

Ответ 1



Если условие задачи допускает использование внешнего просмотрщика, то возьмите бесплатную программу визуализации 3D данных и выводите из вашей программы данные в ее формате. Например, "стандарт индустрии" ParaView.

Ответ 2



Можно использовать библиотеку GLUT #include #include "glut.h" //подключение библиотек импорта (используются директивы, специфичные для MS Visual C++) #pragma comment(lib, "opengl32.lib") #pragma comment(lib, "glu32.lib") #pragma comment(lib, "glut32.lib") //POINT3D - представляет точку в пространстве typedef struct { float x; float y; float z; } POINT3D; //SHAPE3D - представляет трехмерную фигуру typedef struct { POINT3D* points; //массив вершин int count_points; //число вершин } SHAPE3D; SHAPE3D* shapes = NULL; //массив фигур для отображения int n_shapes = 0; //количество фигур void DrawShapes(SHAPE3D* shapes, int n_shapes) //выводит массив фигур { int i=0; int j=0; glBegin(GL_LINES); for(i=0; i

Ответ 3



Чтобы не тратить время на освоение низкоуровневых вещей, вроде OpenGL, можно воспользоваться какой-либо графической библиотекой. Таких библиотек имеется великое множество. Для вывода 2D графики лично я использую либо WinAPI GDI, либо SDL.

воскресенье, 8 декабря 2019 г.

Как узнать об использовании видеокарты приложением?

#cpp #windows #3d #gpu


Необходимо при запуске 3д приложения выполнять код. Думал ставить хуки на создание
окон d3d и opengl, но потом увидел в диспетчере вот такое 


  


Подскажите функцию или пример, как можно вынуть данную информацию.
    


Ответы

Ответ 1



Функциональность просмотра информации о работе GPU в диспетчере - новинка Windows 10 (подробнее). Для того, чтобы она работала, необходим графический адаптер с поддержкой WDDM 2.0. Информация берется, по-видимому, из специального счетчика производительности "GPU Engine", который имеет для каждого процесса, использующего графику, несколько экземпляров с именем вида: pid_5036_luid_0x00000000_0x00009F54_phys_0_eng_3_engtype_3D Из этой строки можно извлечь информацию о том, к какому процессу и графическому движку он относится. У счетчика два значения, Utilization Percentage и Running time. Utilization Percentage у меня почему-то всегда нулевое, наверное, самый простой способ найти процессы, использующие 3D-графику - это отфильтровать те, у которых Running time больше нуля. Для работы со счетчиками производительности можно использовать WMI или PDH-функции. Пример вывода процессов, использующих 3D Engine, с помощью PDH-функций: #define WIN32_LEAN_AND_MEAN 1 #include #include #include #include #include //для работы со счетчиками производительности: #include #include #pragma comment(lib, "pdh.lib") //для получения информации о процессах: #include "psapi.h" #include "Shlwapi.h" #pragma comment(lib, "Shlwapi.lib") const PWSTR COUNTER_OBJECT = L"GPU Engine"; //Имя счетчика производительности /*Вывод имени процесса по его PID*/ void PrintProcessName(int pid){ HANDLE hProcess; DWORD charsCarried=0; TCHAR PrName[MAX_PATH]=L""; hProcess= OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,FALSE,pid); if (hProcess) { charsCarried=MAX_PATH; if (QueryFullProcessImageName(hProcess,0,PrName,&charsCarried)==0){ StringCchCopy(PrName,MAX_PATH,L"Unknown process"); } CloseHandle(hProcess); } else StringCchCopy(PrName,MAX_PATH,L"Unknown process"); PathStripPath(PrName); wprintf(L"%s",PrName); } void main(void) { PDH_STATUS status = ERROR_SUCCESS; LPWSTR pwsCounterListBuffer = NULL; DWORD dwCounterListSize = 0; LPWSTR pwsInstanceListBuffer = NULL; DWORD dwInstanceListSize = 0; LPWSTR pTemp = NULL; HQUERY hQuery = NULL; HCOUNTER hCounter = NULL; DWORD dwFormat = PDH_FMT_LONG; PDH_FMT_COUNTERVALUE ItemBuffer; wchar_t buffer[1000]; wchar_t * p; // Determine the required buffer size for the data. status = PdhEnumObjectItems( NULL, // real-time source NULL, // local machine COUNTER_OBJECT, // object to enumerate pwsCounterListBuffer, // pass NULL and 0 &dwCounterListSize, // to get required buffer size pwsInstanceListBuffer, &dwInstanceListSize, PERF_DETAIL_WIZARD, // counter detail level 0); if (status == PDH_MORE_DATA) { // Allocate the buffers and try the call again. pwsCounterListBuffer = (LPWSTR)malloc(dwCounterListSize * sizeof(WCHAR)); pwsInstanceListBuffer = (LPWSTR)malloc(dwInstanceListSize * sizeof(WCHAR)); if (NULL != pwsCounterListBuffer && NULL != pwsInstanceListBuffer) { status = PdhEnumObjectItems( NULL, // real-time source NULL, // local machine COUNTER_OBJECT, // object to enumerate pwsCounterListBuffer, &dwCounterListSize, pwsInstanceListBuffer, &dwInstanceListSize, PERF_DETAIL_WIZARD, // counter detail level 0); if (status == ERROR_SUCCESS) { wprintf(L"\nProcesses using 3D engine:\n\n"); // Walk the instance list. The list can contain one // or more null-terminated strings. The list is terminated // using two null-terminator characters. for (pTemp = pwsInstanceListBuffer; *pTemp != 0; pTemp += wcslen(pTemp) + 1) { p = wcsstr(pTemp,L"engtype_3D"); if(p==NULL)continue;//отфильтровать записи, не относящиеся к 3D Engine //построим путь к запрашиваемому значению счетчика... StringCchCopy(buffer,1000,L"\\GPU Engine("); StringCchCat(buffer,1000,pTemp); StringCchCat(buffer,1000,L")\\Running time"); //создадим запрос... status = PdhOpenQuery(NULL, 0, &hQuery); if (ERROR_SUCCESS != status) { wprintf(L"PdhOpenQuery failed with 0x%x\n", status); continue; } //добавим счетчик в запрос... status = PdhAddCounter(hQuery, buffer, 0, &hCounter); if (ERROR_SUCCESS != status) { wprintf(L"PdhAddCounter failed with 0x%x\n", status); continue; } //получаем данные... status = PdhCollectQueryData(hQuery); if (ERROR_SUCCESS != status) { wprintf(L"PdhCollectQueryData failed with 0x%x\n", status); continue; } status = PdhCollectQueryData(hQuery); if (ERROR_SUCCESS != status) { wprintf(L"PdhCollectQueryData failed with 0x%x\n", status); continue; } // Format the performance data record. status = PdhGetFormattedCounterValue(hCounter, dwFormat,(LPDWORD)NULL,&ItemBuffer); if (ERROR_SUCCESS != status) { wprintf(L"PdhGetFormattedCounterValue failed with 0x%x\n", status); continue; } int pid=0; //выведем информацию о процессе, если Running time > 0 if(ItemBuffer.longValue > 0){ swscanf(pTemp,L"pid_%d",&pid); //парсим PID из имени счетчика PrintProcessName(pid); wprintf(L", PID: %d - ", pid); wprintf(L"Usage time: %d\n",(int)ItemBuffer.longValue); } PdhCloseQuery(hQuery); } } else { wprintf(L"Second PdhEnumObjectItems failed with %0x%x.\n", status); } } else { wprintf(L"Unable to allocate buffers.\n"); status = ERROR_OUTOFMEMORY; } } else { wprintf(L"\nPdhEnumObjectItems failed with 0x%x.\n", status); } if (pwsCounterListBuffer != NULL) free (pwsCounterListBuffer); if (pwsInstanceListBuffer != NULL) free (pwsInstanceListBuffer); system("PAUSE"); } На системах до Windows 10 (или если нет поддержки данной функциональности у адаптера, и, как следствие - нет этого счетчика), можно использовать функцию EnumProcessModules для получения загруженных DLL процесса. Если среди них есть библиотеки Direct 3D - d3d9.dll, d3d10.dll, d3d11.dll и т.п., можно считать, что процесс использует 3D-графику.