Я хотел бы поделиться некоторыми мыслями о том, как three.js как фреймворк вписывается в «общую картину». Three.js делает много вещей, и это может несколько сбивать с толку, как он соотносится с другими трехмерными полями. Его масштабы также постоянно развиваются, поэтому не так просто резюмировать его, и эти наблюдения субъективны.

3D-библиотека JavaScript

Цель проекта - создать простую в использовании, легкую 3D-библиотеку. Библиотека предоставляет средства визуализации ‹canvas›, ‹svg›, CSS3D и WebGL.

Это официальное описание из репозитория github. На самом деле он довольно хорошо резюмирует, но каждая тема в этом предложении представляет собой отдельную обширную тему, и это еще не все, что делает three.js.

Давайте проанализируем это описание:

JavaScript 3D-библиотека

Сама библиотека написана на javascript и предназначена для использования в среде javascript. По большей части это означает, что он будет запускаться на стороне клиента в веб-браузере на каком-то устройстве. Но с node.js и безголовыми браузерами его также можно использовать на стороне сервера. Первая мысль, которая приходит в голову, - это рендеринг - возможно, несколько скриншотов для предварительного просмотра на сервере, но это также может быть просто какое-то трехмерное вычисление, поскольку у three.js есть богатая математическая библиотека.

JavaScript 3D-библиотека

Это очень широкий термин. 3D может означать многое. По большей части мы думаем о «графике».

Большинство проектов three.js, которые мы видим, включают трехмерную графику в реальном времени, где взаимодействие пользователя приводит к немедленной визуальной обратной связи. Другой тип трехмерной графики - это либо различные эффекты, либо искусственные персонажи в фильмах, либо различные «визуализации», которые вы можете увидеть в распечатанном виде или в веб-каталоге (например, веб-сайт IKEA полон трехмерной графики, поскольку все снимки их продуктов были созданы на компьютере. ). Я видел, как это называется «оффлайн (3D) рендеринг».

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

Three.js поставляется с собственной математической библиотекой со специальными классами для трехмерной математики. Существуют автономные библиотеки, которые занимаются только этой математикой, но с тремя, это всего лишь подмножество гораздо более крупной системы.

Библиотека предоставляет средства визуализации ‹canvas›, ‹svg›, CSS3D и WebGL.

Рендеринг - еще одна большая обязанность библиотеки, но и здесь все становится немного сложнее. WebGL довольно особенный и выделяется из этой группы.

В случае с canvas, svg и css, три полностью отвечают за 3D-рендеринг. Эти API-интерфейсы имеют много других библиотек для рисования не трехмерных элементов, или фактически делают это по умолчанию (css рисует двухмерные прямоугольники, холст с различными двухмерными фигурами), но для этого требуется немного магии и трехмерная математика. 3D-рендеринг.

Прикосновение магии в основном проявляется в абстракции интерфейса. Например, довольно сложно управлять трехмерным состоянием элемента ‹div›, который преобразуется в трехмерное изображение с помощью CSS. Требуется много логики, чтобы заставить API Canvas рисовать что-то похожее на 3D. WebGL на несколько порядков сложнее.

Three абстрагирует все эти API до чего-то столь же простого, как render(), но для этого требуется общее представление о том, что такое «трехмерный мир».

График сцены

Можно выделить область three.js, которая служит этой общей абстракцией трехмерного мира. График сцены - это структура данных, которая используется для описания того, как объекты в некоторой трехмерной сцене (мире) соотносятся друг с другом. На самом деле он не обязательно должен быть трехмерным, так как это подходящий способ описания любой иерархии векторной графики. В частности, это дерево, состоящее из узлов с корневым узлом, который разветвляется. В three.js базовым классом для этой структуры данных является Object3D.

Это почти то же самое, что и дерево DOM. THREE.Scene был бы аналогичен <body>, а все остальное - ветки. В DOM мы можем позиционировать вещи, но они довольно ограничены. Вращение обычно происходит вокруг одной оси, и мы перемещаем вещи влево / вправо или вверх / вниз. В графике трехмерной сцены у нас больше степеней свободы.

Сцена Three больше похожа на виртуальный DOM. Мы выполняем наши операции и устанавливаем состояние в этом дереве, и когда нам требуется визуальный снимок этого состояния (например, в непрерывном цикле или при некотором взаимодействии с пользователем / изменении состояния), мы вызываем render(scene). Вы не хотите обновлять все дерево DOM, когда что-то меняется, в то время как с элементом ‹canvas› мы должны очистить все представление, а затем перерисовать все, даже если только один элемент изменил положение.

‹Div› внутри ‹div› было бы аналогично родительско-дочерним отношениям THREE.Mesh('sun')->THREE.Mesh('earth'). Правило CSS может быть аналогом THREE.Material, где описание, такое как color:'red', вызывает волшебство и что-то окрашивается в красный цвет. Наконец, вызов threeRenderer.render(scene) может быть аналогичен загрузке браузером некоторой html-страницы с некоторыми правилами CSS.

Mesh, Scene, Camera, Light - все подклассы этого общего класса. Это то, что позволяет вам add() преобразовать «коробку» в «сцену» или сделать так, чтобы «свет» следовал за «камерой».

Простая конструкция может быть очень плоской. Корневой узел можно рассматривать как «мир», и он может иметь дочерние элементы «земля», «дом», «солнце», «камера».

THREE.Scene('world')
|-THREE.Mesh('ground')
|-THREE.Mesh('house')
|-THREE.Light('sun')
|-THREE.Camera('main')

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

Результаты могут отличаться, например, CSS ограничивается очень стилизованной визуализацией, тогда как WebGL может отображать тени и общую высокую точность воспроизведения.

Эта структура управляет трехмерным миром. Если бы мы хотели смоделировать, как дневной свет влияет на дом в разное время года, мы бы программно изменили положение и ориентацию света в мире. Задача графа сцены - раскрыть этот хук, то есть «позицию», но для того, чтобы действительно оживить его, вам придется реализовать свою собственную логику. Простой способ анимировать трехмерную сцену three.js - использовать «анимационную» библиотеку.

Все это, вероятно, верно только в теории, и вы не сможете просто переключать рендереры на сцене по своему усмотрению. Но в основном это происходит из-за перекрытия «материалов» с графом сцены и их связи с рендерерами. Например, не существует способа, чтобы ‹div› отбрасывал тень или выглядел как металл, что может описать материал WebGL, но можно сделать его «красным», на что способны все материалы.

Под этим все Object3D все еще является общим, а пространственные и иерархические отношения узлов друг к другу описываются с помощью «графа сцены».

Говоря простым языком, это scene переменная, с которой вы сталкиваетесь после нескольких scene.add(my_foo) вызовов.

WebGL

Webgl является супер-пупер-особенным и, вероятно, используется примерно в 99% из существующих приложений three.js. Это большая тема, поэтому сначала стоит сделать обзор альтернатив.

холст, CSS, SVG

Это все API. Это интерфейс, который вы, как программист, можете использовать, чтобы указать браузеру рисовать определенные объекты.

CSS - наиболее распространенный интерфейс в сети, поскольку без него все выглядело бы как обычный текст. Исторически это не имело ничего общего с 3D.

На самом деле Canvas использует тот же элемент для рисования, что и WebGL, но в другом контексте. Контекст на самом деле называется «2d», но поскольку 3d в любом случае является фальшивым, и мы всегда рисуем на каком-то 2d экране, будь то реальный или виртуальный, мы можем использовать этот контекст также для рисования трехмерной графики.

SVG - это еще один API, не связанный с 3D, обычно используемый для описания таких вещей, как логотипы или значки. Однако, поскольку он может описывать примитивные вещи, такие как линии, они также могут отображаться в контексте 3D (например, наложение на карту или элементы пользовательского интерфейса или HUD с учетом пространства).

Общей чертой здесь является то, что ни один из них не предназначен для использования в 3D.
Еще одна важная черта - все они высокого уровня - уже предназначены для чего-то другого. Например, все трое умеют рисовать «круг». С холстом это явная форма, с CSS вам, возможно, придется использовать радиусы границ, а что нет, но конечный результат - очень прямой доступ к «кругу».

Три переводят эту речь очень высокого уровня на еще одну речь высокого уровня:

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

Низкий уровень

Я хочу сказать, что WebGL ничего не умеет рисовать, но это неправда.

WebGL растрирует примитивы и помещает результаты в буферы.

^ это, с другой стороны, может показаться довольно устрашающим.

WebGL находится на очень низком уровне, он мало что знает о концепции трехмерной графики. Трехмерная графика требует выполнения определенных математических вычислений, и требует их МНОГОЕ. Просто подумайте на секунду о своем экране с высоким разрешением и о том, сколько на нем пикселей. Если вам нужно выполнить вычисление для каждого пикселя, чтобы определить, как свет влияет на поверхность, и вы должны делать это 60 раз в секунду, это число складывается.

Чтобы смягчить это, существует так называемое аппаратное ускорение.

GPU

Большинство компьютеров, таких как ноутбуки, настольные компьютеры, сотовые телефоны и даже часы, имеют какое-то аппаратное устройство, которое может эффективно вычислять эти трехмерные операции и обеспечивать рендеринг интерактивной графики. Это называется графическим процессором.

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

Так же, как мы используем javascript для программирования браузера, мы используем WebGL для программирования видеокарты.

Что ж, концептуально это верно, но на практике это два совершенно разных зверя. WebGL состоит как из кода javascript (команд), так и из совершенно другого языка, который фактически выполняет вычисления (GLSL). Можно провести некоторую параллель между HTML и JavaScript и тем, как они работают вместе на одной странице.

2D и 3D

От этого аппаратного ускорения выигрывает не только 3D. Обработка видео также является хорошим кандидатом. Вы можете запрограммировать графическую карту для преобразования цветов или искажения изображения в прямом видео.

Будучи настолько низким уровнем, WebGL является универсальным. Он ничего не знает о 2d или 3d, но знает о памяти, буферах, очередях команд, шейдерах и т. Д.

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

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

Другая часть API WebGL - это привязки javascript, которые он предоставляет, через которые вы указываете графическому процессору что-то делать. Шейдер «выполняет вычисление A», а привязка «выполняется миллион раз».

Программист должен определить, что вычисление A. Это может быть что-то связанное с 3D или ядро, размывающее видео.

Когда вы начинаете абстрагироваться от этих вычислений и этих команд, вы получаете three.js.

Рендереры работают вместе

Один из вариантов использования, который имеет большой смысл, - это использовать комбинацию модулей рендеринга для рисования вещей, которые у них хорошо получается, «в 3D». WebGL может обрабатывать множество чисел и создавать реалистичные визуальные эффекты с высокой точностью, но плохо обрабатывает текст и даже некоторые строки. Текст визуализации дополнительного слоя можно было контролировать с помощью средств визуализации CSS и холста, а различными путями и линиями - с помощью SVG.

THREE.WebGLRenderer

Все эти низкоуровневые вещи абстрагируются с помощью одного класса three.js WebGLRenderer. Это то, что преобразует куб в набор чисел в памяти графического процессора.

По иронии судьбы, это единственный модуль рендеринга three.js, который не должен заниматься исключительно трехмерной графикой, но лучше всего подходит для этого. Другие имитируют 3D, используя 2D API, WebGL намеренно делает 3D, используя общий API параллельных вычислений. Но это по-прежнему не исключает сценария, в котором вы могли бы использовать его исключительно для обработки этого видеопотока в реальном времени. Он достаточно абстрагируется от WebGL, чтобы сделать его полезным для этой задачи, но вы, вероятно, использовали бы треть библиотеки.

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

Тот факт, что вы будете использовать только треть библиотеки, означает, что может быть другой инструмент, более подходящий для этого варианта использования, или что вы можете создать только подмножество three.js. И для super mario, и для примеров обработки видео, возможно, потребуется только PlaneGeometry и, возможно, один тип Material.

Попробуем повторить:

ТРИ-математика

Код JavaScript, который выполняет математические операции с трехмерной графикой. JS по умолчанию имеет Math.pow(), но не Quaternion.inverse(). С помощью этих классов мы можем писать алгоритмы, которые не нужно рендерить - например, игровой сервер, который проверяет, кто стрелял, который будет выполнять много лучевых вычислений, но ничего не будет рисовать.

ТРЕХСценный граф

Семейство Object3D подклассов, которые образуют древовидную структуру данных, описывающую «трехмерный мир» и взаимосвязь объектов в нем. Концептуально это абстрактно, но может быть несколько связано с конкретным средством визуализации, как только вы погрузитесь в код.

ТРИ-рендерер

Слой, который переводит этот общий график в визуальное представление на каком-то экране или в каком-то буфере (скажем, вы генерируете его на стороне сервера). Для этого используются разные технологии.

ТРИ-WebGLRenderer

Конкретный модуль рендеринга, который позволяет использовать аппаратное ускорение и знает многие концепции 3D, но также может использоваться для 2D (и даже просто общих вычислений).

Это ИМО, основные строительные блоки three.js. Я был бы склонен заменить «3D» на «Графика», но это применимо только в случае WebGLRenderer.

Практические примеры

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

Three.js - это не инструмент для 3D-моделирования

Я загрузил человека с сайта human.obj, но теперь не могу выбрать его руки или ноги.

Three.js знает о «человеке» столько же, сколько вы ему рассказываете. Если вы сказали ему загрузить «human.obj», постарайтесь не думать о нем как о «человеке», а скорее как о «сетке, загруженной из файла».
Представьте, что если вместо получения human.obj вы каким-то образом получите human_arms.obj, human_torso.obj, human_legs.obj и загрузите несколько мешей - это даст вам гораздо больше возможностей для работы. Теперь проблема заключается в «выборе сетки», а не «выборе ступней человека», что фактически означало «выбор вспомогательной сетки (не зная, что это такое)».

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

Тройка тоже не имеет ничего общего с чем-то вроде Open Cascade. Вы можете создать инструмент моделирования, используя three.js, но три из них будут использоваться для рендеринга и, возможно, в качестве строительных блоков для механизма моделирования (математика). Его также можно просто подключить к существующему двигателю.

Three.js - это не игровой движок

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

Все это означало бы больше кода, а для тех, кто просто хочет раскрутить 3D-модель как часть каталога продуктов, это было бы не только ненужным, но и нежелательным.

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

Three.js мало загружается

Конечно, в ядре есть загрузчики для некоторых ресурсов, но все распространенные форматы, такие как gltf или fbx, являются автономными. Тройке все равно, как вы получите свои активы, если вы правильно их проанализируете и создадите THREE объектов.

Что касается трех, то нет разницы между сеткой из файла gltf и процедурной сферой. Многие творческие примеры используют кубы и сферы и не загружают ничего, кроме самого three.js.

Основные загрузчики очень общие, они загружают изображения и файлы, а также прямые представления трех объектов, таких как Material или Texture. Загрузчики для определенных форматов состоят из этих строительных блоков.

Нет никакой проблемы «формат X не работает с three.js». Либо файл недействителен, либо не работает загрузчик.

Примеры three.js - это не three.js

Хотя некоторые примеры three.js считаются само собой разумеющимся как часть three.js, это не так. Типичным примером являются различные элементы управления орбитой. Изначально three.js не знает, как обрабатывать ввод с помощью мыши и как применять логику орбиты к камере.

Короче говоря, если вы перейдете в репозиторий, все, что находится в папке /examples, считается дополнением. Некоторые из них более важны, чем другие, поскольку они представляют собой более распространенные варианты использования. Элементы управления камерой, вероятно, более распространены, чем Octree.

Если вы собираетесь взять с полки что-нибудь из трех связанных и изменить это, вы, вероятно, в конечном итоге измените что-то в /examples, чем в /src. Т.е. более вероятно, что три могут решить проблему без изменений, но этот пример не подходит для вашего варианта использования.

Спасибо за чтение и оставьте, пожалуйста, комментарий :)