Я не 3D-художник. Я не знаю, как использовать Blender или Maya. Вместо этого я создаю трехмерные формы с помощью кода. Однако создать геометрию с нуля довольно сложно. Вам нужно создать серию точек в 3D, не имея возможности предварительно их визуализировать. Гораздо более простое решение - начать с некоторой существующей геометрии и изменить ее. К счастью, в ThreeJS есть множество встроенных геометрических примитивов, которые легко изменить.

Эта статья является частью моей текущей серии руководств по ThreeJS средней сложности. Я давно хотел что-то среднее между уровнями вступления Как нарисовать куб и Давайте заполним экран шейдерным безумием. Итак, вот оно.

Давайте построим из примитивов маленькую сосну.

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

Ясно, что он состоит из нескольких конусов и цилиндра. Вот код, чтобы сложить их вместе:

const group = new THREE.Group()
const level1 = new THREE.Mesh(
    new THREE.ConeGeometry(1.5,2,8),
    new THREE.MeshLambertMaterial({color:0x00ff00})
)
level1.position.y = 4
group.add(level1)
const level2 = new THREE.Mesh(
    new THREE.ConeGeometry(2,2,8),
    new THREE.MeshLambertMaterial({color:0x00ff00})
)
level2.position.y = 3
group.add(level2)
const level3 = new THREE.Mesh(
    new THREE.ConeGeometry(3,2,8),
    new THREE.MeshLambertMaterial({color:0x00ff00})
)
level3.position.y = 2
group.add(level3)
const trunk = new THREE.Mesh(
    new THREE.CylinderGeometry(0.5,0.5,2),
    new THREE.MeshLambertMaterial({color:0xbb6600})
)
trunk.position.y = 0
group.add(trunk)
return group

Оптимизация вызовов розыгрыша

Приведенный выше код работает, но мы, вероятно, не хотим добавлять все эти части каждый раз, когда нам нужно дерево. Мы могли бы объединить их в Group (), но это указывает на новую проблему. Это займет несколько вызовов отрисовки, по одному для каждого объекта. Это своего рода трата. Вместо этого давайте объединим их.

Если вы помните свой первый учебник по Three JS, вы можете вспомнить, что сетки состоят из материала для цвета и текстуры, а также из геометрии для фактической формы. Геометрия трех JS имеет классные способности. Вы можете объединить несколько геометрий вместе, чтобы создать новый объект, а затем добавить один материал. Это имеет то преимущество, что используется только один вызов отрисовки. Давай сделаем это.

const geo = new THREE.Geometry()
const level1 = new THREE.ConeGeometry(1.5,2,8)
level1.translate(0,4,0)
geo.merge(level1)
const level2 = new THREE.ConeGeometry(2,2,8)
level2.translate(0,3,0)
geo.merge(level2)
const level3 = new THREE.ConeGeometry(3,2,8)
level3.translate(0,2,0)
geo.merge(level3)
const trunk = new THREE.CylinderGeometry(0.5,0.5,2)
trunk.translate(0,0,0)
geo.merge(trunk)
return group = new THREE.Mesh(
    geo,
    new THREE.MeshLambertMaterial({color:0x00ff00})
)
return group

Теперь у нас есть только один вызов отрисовки (плюс один для программы просмотра статистики).

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

Несколько цветов

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

Альтернатива - цвета вершин. Сетка на самом деле представляет собой серию буферов. Один буфер содержит вершины, составляющие форму. Другой содержит нормали (как мы использовали в этом блоге). Стандартные примитивы Three JS также поддерживают другой буфер: цвета для каждой грани. Это позволяет нам установить любой цвет для каждой грани геометрии. Вот что нам нужно использовать.

const geo = new THREE.Geometry()
const level1 = new THREE.ConeGeometry(1.5,2,8)
level1.faces.forEach(f => f.color.set(0x00ff00))
level1.translate(0,4,0)
geo.merge(level1)
const level2 = new THREE.ConeGeometry(2,2,8)
level2.faces.forEach(f => f.color.set(0x00ff00))
level2.translate(0,3,0)
geo.merge(level2)
const level3 = new THREE.ConeGeometry(3,2,8)
level3.faces.forEach(f => f.color.set(0x00ff00))
level3.translate(0,2,0)
geo.merge(level3)
const trunk = new THREE.CylinderGeometry(0.5,0.5,2)
trunk.faces.forEach(f => f.color.set(0xbb6600))
trunk.translate(0,0,0)
geo.merge(trunk)
return group = new THREE.Mesh(
    geo,
    new THREE.MeshLambertMaterial({
        vertexColors: THREE.VertexColors,
    })
)
return group

Нам также нужно указать материалу использовать цвета вершин вместо цвета материала.

const mesh = new THREE.Mesh(tree, new THREE.MeshLambertMaterial({
      vertexColors: THREE.VertexColors,
}))

Живая демонстрация

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

Быть замеченным

Кстати, если вы работаете над классным интерфейсом WebVR, который вы хотели бы продемонстрировать прямо в Firefox Reality, дайте нам знать.