Автор оригинала: FreeCodeCamp Community Member.
Собрание сцены 3D в браузере с Threy.js – это как играть с Legos. Мы собрали некоторые коробки, добавляйте огни, определите камеру, и Three.js отображает 3D-образ.
В этом руководстве мы собираемся собрать минималистичный автомобиль из коробок и научиться отображать текстуру на него.
Во-первых, мы сделаем вещи – мы определим огни, камеру и рендерер. Затем мы узнаем, как определить геометрию и материалы для создания трехмерных объектов. И, наконец, мы собираемся кодовые текстуры с помощью JavaScript и HTML Canvas.
Как настроить проект Three.js
Threy.js – это внешняя библиотека, поэтому сначала нам нужно добавить его в наш проект. Я использовал NPM, чтобы установить его в мой проект, затем импортировал его в начале файла JavaScript.
import * as THREE from "three"; const scene = new THREE.Scene(); . . .
Во-первых, нам нужно определить сцену. Сцена – это контейнер, который содержит все 3D-объекты, которые мы хотим отображать вместе с огнями. Мы собираемся добавить машину на эту сцену, но сначала давайте настроим огни, камеру и рендерер.
Как настроить свет
Мы добавим два света на сцену: окружающий свет и направленный свет. Мы определяем как, устанавливая цвет и интенсивность.
Цвет определяется как шестнадцатеричное значение. В этом случае мы устанавливаем его на белый. Интенсивность – это число от 0 до 1, и, как оба из них сияют одновременно, мы хотим, что эти значения где-то около 0,5.
. . . const ambientLight = new THREE.AmbientLight(0xffffff, 0.6); scene.add(ambientLight); const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8); directionalLight.position.set(200, 500, 300); scene.add(directionalLight); . . .
Окружающий свет сияет из каждого направления, давая базовый цвет для нашей геометрии, в то время как направленный свет имитирует солнце.
Направленная свет сияет от очень далеко с параллельными световыми лучами. Мы устанавливаем позицию для этого света, который определяет направление этих световых лучей.
Эта позиция может быть немного запутана, поэтому позвольте мне объяснить. Из всех параллельных лучей мы определяем одно в частности. Этот конкретный светлый луч будет светить от позиции, который мы определяем (200 500 300) до 0,0,0 координаты. Остальные будут параллельно этому.
Поскольку световые лучи параллельны, и они сияют от очень далеко, точные координаты здесь не имеют значения – скорее, их пропорции делают.
Три параметра позиции являются координатами x, y и z. По умолчанию ось Y указывает вверх, и, поскольку имеет наибольшее значение (500), что означает, что верхняя часть нашего автомобиля получает наибольшее количество света. Так что это будет самым ярким.
Два других значения определяют, насколько света согнута вдоль оси X и Z, то есть насколько светится спереди, а сторона автомобиля получит.
Как настроить камеру
Далее давайте настроим камеру, которая определяет, как мы смотрим на эту сцену.
Здесь есть два варианта – перспективные камеры и ортографические камеры. Видеоигры в основном используют перспективные камеры, но мы собираемся использовать орфографический, чтобы иметь более минимальный, геометрический вид.
В моей предыдущей статье мы обсудили различия между двумя камерами более подробно. Поэтому в этом мы обсудим только как настроить орфографическую камеру.
Для камеры нам нужно определить вид на просмотр. Это регион в трехмерном пространстве, который будет проецироваться на экран.
В случае орфографической камеры это коробка. Камера проецирует 3D-объекты внутри этой коробки к одной из его сторон. Поскольку каждая проекционная линия находится параллельно, ортографические камеры не искажают геометрию.
. . . // Setting up camera const aspectRatio = window.innerWidth / window.innerHeight; const cameraWidth = 150; const cameraHeight = cameraWidth / aspectRatio; const camera = new THREE.OrthographicCamera( cameraWidth / -2, // left cameraWidth / 2, // right cameraHeight / 2, // top cameraHeight / -2, // bottom 0, // near plane 1000 // far plane ); camera.position.set(200, 200, 200); camera.lookAt(0, 10, 0); . . .
Чтобы настроить орфографическую камеру, мы должны определить, насколько далеко каждая сторона Frustum с точки зрения. Определим, что левая сторона составляет 75 единиц влево, правая плоскость составляет 75 единиц вправо, и так далее.
Здесь эти единицы не представляют пиксели экрана. Размер отображаемого изображения будет определен в рендерере. Здесь эти значения имеют произвольную единицу, которую мы используем в трехмерном пространстве. Позже, при определении трехмерных объектов в трехмерном пространстве мы собираемся использовать те же блоки для установки их размера и положения.
Как только мы определим камеру, нам также нужно поместить ее и повернуть ее в направлении. Мы перемещаем камеру на 200 единиц в каждом измерении, тогда мы устанавливаем его, чтобы оглянуться на координату 0,10,0. Это почти в происхождении. Мы смотрим на точку зрения немного над землей, где будет центр нашего автомобиля.
Как настроить рендерер
Последнее произведение, которое нам нужно настроить, – это рендерер, который делает сцену в соответствии с нашей камерой в наш браузер. Мы определяем WebGlrenderer, как это:
. . . // Set up renderer const renderer = new THREE.WebGLRenderer({ antialias: true }); renderer.setSize(window.innerWidth, window.innerHeight); renderer.render(scene, camera); document.body.appendChild(renderer.domElement);
Здесь мы также устанавливаем размер холста. Это единственное место, где мы устанавливаем размер в пикселях, так как мы устанавливаем, как оно должно появиться в браузере. Если мы хотим заполнить все окно браузера, мы передаем размер окна.
И, наконец, последняя строка добавляет это отображение наш HTML-документ. Он создает элемент HTML Canvas для отображения отображаемого изображения и добавляет его к DOM.
Как построить машину в три.
Теперь посмотрим, как мы можем составить машину. Во-первых, мы создадим автомобиль без текстуры. Это будет минималистичный дизайн – мы просто собрали четыре ящики.
Как добавить коробку
Во-первых, мы создаем пару колес. Мы определим серую коробку, которая представляет собой левое и правильное колесо. Поскольку мы никогда не видим машину снизу, мы не заметим, что вместо того, чтобы вместо того, чтобы иметь отдельное левое и правое колесо, у нас есть только одна большая коробка.
Нам понадобится пара колеса как спереди, так и на задней части автомобиля, поэтому мы можем создать многоразовую функцию.
. . . function createWheels() { const geometry = new THREE.BoxBufferGeometry(12, 12, 33); const material = new THREE.MeshLambertMaterial({ color: 0x333333 }); const wheel = new THREE.Mesh(geometry, material); return wheel; } . . .
Мы определяем колесо как сетка. Сетка – это сочетание геометрии и материала, и он будет представлять наш трехмерный объект.
Геометрия определяет форму объекта. В этом случае мы создаем коробку по настройкам его размеров вдоль оси x, y и z 12, 12 и 33 единицами.
Затем мы передаем материал, который будет определять появление нашей сетки. Есть разные варианты материала. Основное отличие между ними заключается в том, как они реагируют на свет.
В этом руководстве мы будем использовать Meshambertmaterial
Отказ Meshambertmaterial
рассчитывает цвет для каждой вершины. В случае рисования коробки, это в основном каждую сторону.
Мы можем видеть, как это работает, так как каждая сторона коробки имеет другой оттенок. Мы определили направленный свет, чтобы светить в основном сверху, поэтому верхняя часть коробки является самым ярким.
Некоторые другие материалы рассчитают цвет, не только для каждой стороны, но для каждого пикселя в стороне. Они приводят к более реалистичным изображениям для более сложных форм. Но для ящиков, освещенных направленным светом, они не имеют большого значения.
Как построить остальную часть автомобиля
Тогда подобным образом давайте давайте создадим остальную часть автомобиля. Мы определяем CreateCar
Функция, которая возвращает группу. Эта группа – другой контейнер, как сцена. Он может держать объекты Three.js. Это удобно, потому что если мы хотим двигаться вокруг машины, мы можем просто переместить вокруг группы.
. . . function createCar() { const car = new THREE.Group(); const backWheel = createWheels(); backWheel.position.y = 6; backWheel.position.x = -18; car.add(backWheel); const frontWheel = createWheels(); frontWheel.position.y = 6; frontWheel.position.x = 18; car.add(frontWheel); const main = new THREE.Mesh( new THREE.BoxBufferGeometry(60, 15, 30), new THREE.MeshLambertMaterial({ color: 0x78b14b }) ); main.position.y = 12; car.add(main); const cabin = new THREE.Mesh( new THREE.BoxBufferGeometry(33, 12, 24), new THREE.MeshLambertMaterial({ color: 0xffffff }) ); cabin.position.x = -6; cabin.position.y = 25.5; car.add(cabin); return car; } const car = createCar(); scene.add(car); renderer.render(scene, camera); . . .
Мы генерируем две пары колес с нашей функцией, а затем определите основную часть автомобиля. Затем мы добавим верхнюю часть салона как четвертая сетка. Все это просто коробки с разными размерами и разными цветами.
По умолчанию каждая геометрия будет в середине, а их центры будут в координате 0,0,0.
Во-первых, мы поднимаем их, регулируя их положение вдоль оси Y. Мы поднимаем колеса на половину их роста – поэтому вместо того, чтобы погружаться на полпути к земле, они лежали на земле. Затем мы также отрегулируем куски вдоль оси X, чтобы достичь их окончательной позиции.
Мы добавляем эти части в группу автомобилей, затем добавьте всю группу на сцену. Важно, чтобы мы добавили машину на сцену, прежде чем рендурировать изображение, или нам нужно позвонить снова, как только мы модифицировали сцену.
Как добавить текстуру к автомобилю
Теперь, когда у нас есть наша самая основная модель автомобиля, давайте добавим некоторые текстуры в салон. Мы собираемся покрасить окна. Мы определим текстуру по бокам и один для передней и задней части салона.
Когда мы устанавливаем внешний вид сетки с материалом, установка цвета не является единственным вариантом. Мы также можем сопоставить текстуру. Мы можем предоставить одну и ту же текстуру для каждой стороны, или мы можем предоставить материал для каждой стороны в массиве.
В качестве текстуры мы могли бы использовать изображение. Но вместо этого мы собираемся создавать текстуры с помощью JavaScript. Мы собираемся кодовые изображения с HTML Canvas и JavaScript.
Прежде чем мы продолжим, нам нужно сделать некоторые различия между Try.js и html Canvas.
Three.js – это библиотека JavaScript. Он использует WebGL под капотом для рендеринга 3D-объектов в изображение, и он отображает конечный результат в элементе холста.
HTML Canvas, с другой стороны, это HTML-элемент, как и Div
элемент или тег абзаца. Что делает его особенным, хотя мы можем рисовать фигуры на этом элементе с помощью JavaScript.
Вот как Three.js делает сцену в браузере, и именно так мы собираемся создавать текстуры. Давайте посмотрим, как они работают.
Как нарисовать на HTML Canvas
Нарисовать на холсте, сначала нам нужно создать элемент холста. Хотя мы создаем HTML-элемент, этот элемент никогда не будет частью нашей HTML-структуры. Сам по себе он не будет отображаться на странице. Вместо этого мы превратим его в текстуру TREY.JS.
Посмотрим, как мы можем нарисовать на этом холсте. Во-первых, мы определяем ширину и высоту холста. Размер здесь не определяет, насколько большой холст появится, это больше похоже на разрешение холста. Текстура будет растягиваться в сторону коробки, независимо от его размера.
function getCarFrontTexture() { const canvas = document.createElement("canvas"); canvas.width = 64; canvas.height = 32; const context = canvas.getContext("2d"); context.fillStyle = "#ffffff"; context.fillRect(0, 0, 64, 32); context.fillStyle = "#666666"; context.fillRect(8, 8, 48, 24); return new THREE.CanvasTexture(canvas); }
Затем мы получаем 2D контекст рисования. Мы можем использовать этот контекст для выполнения команд рисования.
Во-первых, мы собираемся заполнить весь холст с белым прямоугольником. Для этого сначала мы установили стиль заполнения. Затем заполните прямоугольник, устанавливая его верхнее положение и его размер. При рисовании на холсте по умолчанию координата 0,0 будет в верхнем левом углу.
Затем мы заполняем другой прямоугольник серого цвета. Этот запускается на 8,8 координата, и он не заполняет холст, он только рисует окна.
И вот и это – последняя строка превращает элемент холста в текстуру и возвращает его, поэтому мы можем использовать его для нашей машины.
function getCarSideTexture() { const canvas = document.createElement("canvas"); canvas.width = 128; canvas.height = 32; const context = canvas.getContext("2d"); context.fillStyle = "#ffffff"; context.fillRect(0, 0, 128, 32); context.fillStyle = "#666666"; context.fillRect(10, 8, 38, 24); context.fillRect(58, 8, 60, 24); return new THREE.CanvasTexture(canvas); }
Аналогичным образом мы можем определить боковую текстуру. Мы снова создаем элемент холста, мы получаем его контекст, а затем сначала заполните весь холст, чтобы иметь базовый цвет, а затем нарисовать окна в виде прямоугольников.
Как сопоставлять текстуры в коробку
Теперь посмотрим, как мы можем использовать эти текстуры для нашей машины. Когда мы определяем сетку для верхней части салона, вместо того, чтобы установить только один материал, мы устанавливаем один для каждой стороны. Мы определяем массив из шести материалов. Мы отображаем текстуры к сторонам салона, в то время как верх и снизу все равно будет иметь простой цвет.
. . . function createCar() { const car = new THREE.Group(); const backWheel = createWheels(); backWheel.position.y = 6; backWheel.position.x = -18; car.add(backWheel); const frontWheel = createWheels(); frontWheel.position.y = 6; frontWheel.position.x = 18; car.add(frontWheel); const main = new THREE.Mesh( new THREE.BoxBufferGeometry(60, 15, 30), new THREE.MeshLambertMaterial({ color: 0xa52523 }) ); main.position.y = 12; car.add(main); const carFrontTexture = getCarFrontTexture(); const carBackTexture = getCarFrontTexture(); const carRightSideTexture = getCarSideTexture(); const carLeftSideTexture = getCarSideTexture(); carLeftSideTexture.center = new THREE.Vector2(0.5, 0.5); carLeftSideTexture.rotation = Math.PI; carLeftSideTexture.flipY = false; const cabin = new THREE.Mesh(new THREE.BoxBufferGeometry(33, 12, 24), [ new THREE.MeshLambertMaterial({ map: carFrontTexture }), new THREE.MeshLambertMaterial({ map: carBackTexture }), new THREE.MeshLambertMaterial({ color: 0xffffff }), // top new THREE.MeshLambertMaterial({ color: 0xffffff }), // bottom new THREE.MeshLambertMaterial({ map: carRightSideTexture }), new THREE.MeshLambertMaterial({ map: carLeftSideTexture }), ]); cabin.position.x = -6; cabin.position.y = 25.5; car.add(cabin); return car; } . . .
Большинство из этих текстур будут сопоставлены правильно без каких-либо корректировок. Но если мы превратим машину вокруг, то мы можем видеть окна в неправильном порядке слева.
Это ожидается, когда мы используем текстуру для правой стороны здесь. Мы можем определить отдельную текстуру для левой стороны или мы можем отразить правую сторону.
К сожалению, мы не можем перевернуть текстуру горизонтально. Мы можем только перевозить текстуру вертикально. Мы можем исправить это в 3 шага.
Во-первых, мы превращаем текстуру на 180 градусов, что равно Pi в радианах. Прежде чем повернуть его, мы должны убедиться, что текстура вращается вокруг его центра. Это не значение по умолчанию – мы должны установить, что центр вращения на полпути. Мы устанавливаем 0,5 на обоих осях, которые в основном означает 50%. Затем, наконец, мы переверните текстуру вверх ногами, чтобы иметь его в правильном положении.
Заворачивать
Так что мы здесь сделали? Мы создали сцену, которая содержит нашу машину и свет. Мы построили автомобиль из простых коробок.
Вы можете подумать, что это слишком простое, но если вы думаете об этом, многие мобильные игры со стильными взглядами на самом деле создаются с помощью коробок. Или просто подумайте о Minecraft, чтобы увидеть, как далеко вы можете провести коробки.
Затем мы создали текстуры с HTML Canvas. HTML Canvas способен намного больше, чем мы использовали здесь. Мы можем нарисовать различные формы с кривыми и дугами, но затем иногда минимальный дизайн – это все, что нам нужно.
И, наконец, мы определили камеру, чтобы установить, как мы смотрим на эту сцену, а также рендерер, что делает окончательное изображение в браузере.
Следующие шаги
Если вы хотите играть с кодом, вы можете найти исходный код на Кодепен Отказ И если вы хотите продвинуться вперед с этим проектом, посмотрите мое видео YouTube о том, как превратить это в игру.
В этом руководстве мы создаем игру трафика. После определения автомобиля мы рисуем гоночную дорожку, мы добавляем игровую логику, обработчики событий и анимацию.