javascript

3D-model (векторизация)

  • вторник, 29 октября 2024 г. в 00:00:04
https://habr.com/ru/articles/853980/

Создание 3d-models с помощью программы на Tree.js и HTML

Содержание

  1. Введение

  2. Создание HTML файла

  3. Создание JSON и Webpack файлов

  4. Создание JS файла

Введение

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

  • Научиться базовым принципам работы с библиотекой three.js;

  • Возможность конвертировать картинки разных форматов в 3d визуал.

Создание HTML файла

Так как мы создаем проект именно в качестве веб-приложения, нам необходимо создать html файл и прописать в нем:

  1. пути к дополнительным ресурсам (файлам);

  2. canvas — это HTML-элемент, который используется для рисования графики с помощью JavaScript;

  3. тег input, необходимый для выбора интересующей пользователя картинки.

<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link href="lab.css" rel="stylesheet">
    <title>3D-Project</title>
</head>
<body>
    <canvas id="myscene"></canvas>
    <input type="file" id="fileInput" accept="image/*">
    <script type="module" src="./lab.js"></script>
</body>
</html>

Создание JSON и Webpack файлов

Начнем с того, что такое файл JSON.


Файл package.json используется в проектах на JavaScript для управления зависимостями, скриптами и метаданными проекта. Он обычно создается в корневой директории нашего проекта и содержит информацию о проекте, а также о необходимых пакетах и командах для его сборки и запуска.


Для начала установите Node.js, если у вас его еще нет, а так же установим webpack (чуть позже он нам пригодится). Команда для macOS:


brew install node

npm install --save-dev webpack webpack-cli


После этого мы запускаем команду npm init -y, которая создает нам файл json с базывыми настройками.

После, мы меняем содержимое файла на это:

    "name": "название нашего файла",
    "version": "1.0.0",
    "scripts": {
        "start": "webpack serve --open",
        "build": "webpack"
    },
    "devDependencies": {
        "html-webpack-plugin": "^5.6.2",
        "three": "^0.169.0",
        "webpack": "^5.95.0",
        "webpack-cli": "^5.1.4",
        "webpack-dev-server": "^5.1.0"
    }
} 

Чтобы создать файл конфигурации для Webpack, нам нужно создать файл с именем webpack.config.js в корневой директории нашего проекта. Этот файл будет содержать настройки, которые Webpack будет использовать для сборки нашего проекта.

const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    entry: './lab.js', // Указывает входной файл для сборки
    output: {
        filename: 'lab.js', // Имя выходного файла
        path: path.resolve(__dirname, 'dist'), // Путь к выходной директории
        clean: true, // Очищает выходную директорию перед каждой сборкой
    },
    devServer: {
        static: {
            directory: path.join(__dirname, 'dist'), // Указывает директорию для статических файлов
        },
        open: true, // Автоматически открывает браузер при запуске сервера
        port: 8080, // Порт, на котором будет запущен сервер
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: './lab.html', // Шаблон HTML-файла
        }),
    ],
    mode: 'development', // Режим сборки (development или production)
};

Создание JS файла

Как вы уже поняли из прошлого блока, нам необходимо создать файл lab.js.
Для начала, рекомендую в принципе ознакомиться с библиотекой Three.js на официальном сайте https://threejs.org. Там прекрасно описаны основные "3 кита Three.js" и есть каталог со всеми необходимыми методами.


Я постараюсь кратко описать всю структуру и математику проекта, поэтому опишу основной алгоритм выполнения:

1) Необходимо скачать библиотеку three так же через brew;

2) Импортируем все из этой библиотеки (это уже прописываем в файле) import * as THREE from 'three';;

3) Для возможности манипулировать в дальнейшем картинкой (вращать ее), прописываем : import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';

4) Создаем необходимый холст, где будет проецироваться 3d-фигура:
let canvas = document.getElementById('myscene'); let width = 1400; let height = 800;

5) Создаем экземпляр рендеринга, задавая ему значение холста и сглаживания (для 3d графики очень важный аспект).

6) Устанавливаем соотношение пикселей с помощью метода setPixelRatio ( возвращает коэффициент пикселей устройства. Если он больше 1 (что означает, что устройство имеет высокое разрешение), мы устанавливаем соотношение пикселей в 2, иначе — в 1.). Потом задаем размеры холста и его цвет

7) Затем создаем сцену и устанавливаем камеру (это базовые настройки, которые можно корректировать в зависимости от наших предпочтений, поэтому расписывать это не буду)

8) Чтобы в дальнейшем не добавлять по одному элементу на сцену, я создам группу, которая будет хранить в себе все элементы моего рисования (далее это поможет при вращении и перемещении всего объекта в целом) let group = new THREE.Group();

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


Распишу это немного подробнее.


Для начала мы создаем переменную которая будет используется для получения первого файла, выбранного пользователем через элемент в HTML.


После того, как мы убедились, что файл не пустой, мы начинаем отрисовку:

  1. Создание объекта изображения: const img = new Image(); Здесь создается новый объект Image, который будет использоваться для загрузки изображения.

  2. Обработчик события onload:

img.onload = function() {
    // Код внутри этой функции выполнится, когда изображение будет загружено
};

Этот обработчик срабатывает, когда изображение успешно загружено. Внутри него будет выполняться основной код для обработки изображения.

  1. Создание канваса и контекста:

let ctx = canvas2d.getContext('2d');
canvas2d.width = 200;
canvas2d.height = 200;

Здесь создается временный элемент , который используется для рисования изображения в 2D. Устанавливаются размеры канваса на 200x200 пикселей.

  1. Рисование изображения на канвасе:

ctx.drawImage(img, 0, 0, 200, 200);
let data = ctx.getImageData(0, 0, size, size).data;

Изображение рисуется на канвасе, и затем получаются данные пикселей с помощью getImageData. Эти данные содержат информацию о цветах каждого пикселя в формате RGBA.

  1. Очистка группы:

group.clear();

Здесь очищается группа group, чтобы удалить предыдущие объекты перед добавлением новых.

  1. Цикл для создания 3D-объектов:

for (let i = 0; i < size; ++i) {
    let geometry = new THREE.BufferGeometry();
    let vertices = new Float32Array(size * 3);
    let colors = new Float32Array(size * 3);

В этом цикле создается новая геометрия для каждой строки пикселей. vertices и colors — это массивы, которые будут содержать координаты вершин и цвета для каждой линии.

  1. Цикл для обработки пикселей:

for (let j = 0; j < size; ++j) {
    let colorIndex = (j * size + i) * 4; 
    let r = data[colorIndex] / 255; 
    let g = data[colorIndex + 1] / 255; 
    let b = data[colorIndex + 2] / 255;

Внутренний цикл проходит по каждому пикселю в строке, извлекая значения цвета (красный, зеленый, синий) из массива данных пикселей. Значения нормализуются, деля на 255.

  1. Установка координат вершин и цветов:

vertices[j * 3] = j - 100; 
vertices[j * 3 + 1] = i - 100; 
vertices[j * 3 + 2] = data[colorIndex] / 10; 

colors[j * 3] = r;
colors[j * 3 + 1] = g; 
colors[j * 3 + 2] = b;

Здесь устанавливаются координаты вершин для 3D-объектов. z-координата устанавливается на основе значения красного канала, деленного на 10, чтобы создать некоторую высоту.

  1. Создание геометрии и добавление в группу:

geometry.setAttribute('position', new THREE.BufferAttribute(vertices, 3));
geometry.setAttribute('color', new THREE.BufferAttribute(colors, 3));

let material = new THREE.LineBasicMaterial({ vertexColors: true });
let line = new THREE.Line(geometry, material);
group.add(line);

После того, как массивы vertices и colors заполнены, они устанавливаются как атрибуты геометрии. Затем создается материал с использованием цветов вершин, и линия добавляется в группу.


В принципе, это все. Нам осталось загрузить результат в объект img и отрисовать это все с помощью функции, которая вызывает саму себя и обновляет данные сцены и камеры:

function animation() {
    requestAnimationFrame(animation);
    controls.update();
    renderer.render(scene, camera);
}
animation();

Осталось только запустить наш проект командой npm start

Надеюсь, эта статья была полезной, и вам так же захочется развиваться в frontend-разработке, как и мне))

Данная статья была написана в соавторстве с @MedveDux