Это вторая часть проекта, в которой мы накладываем динамический 2d-паттерн на 3d-объект. Вы можете начать с первой части (Часть 1) или просто скопировать итоговый код несколькими абзацами ниже.
Некоторое время назад я создал паттерн, который генерировался при помощи нескольких строк кода в библиотеке p5.js.
p5.js — это библиотека для Javascript, созданная в 2014 году. Ее основная цель — стать окном в мир программирования для дизайнеров, художников, учителей и прочих представителей творческих профессий. Он поддерживает и анимацию, и схемы, легко переводится в веб-формат.
Сейчас я расскажу, как сделать финальную часть — наложить паттерн на 3d-объект (в данном случае — куб).
Для начала работы с нуля надо создать проект в онлайн-редакторе editor.p5js.org Мы продолжаем изменять следующий код:
let r=10
let a=0
let с=20
function setup() {
background(220);
createCanvas(400, 400);
}
function draw() {
translate(200,200)
let x = r+с*cos(a);
let y = r+с*sin(a);
fill(255,0,0);
ellipse (x,y,10,10);
с = с + 0.06
a = a + 0.3
}
Чтобы сделать наш холст объемным, нам надо добавить параметр WEBGL в функцию, создающую наш канвас: createCanvas(400,400,WEBGL). Поскольку точка отсчета для такого объекта и так находится в центре, нам можно (и нужно!) удалить translate(200,200), который выполнял корректировку при плоском изображении.
Чтобы разнообразить цветовую гамму и сделать более эффектное изображение, давайте заменим параметры fill() на наши переменные r,a,c — теперь с каждой новой итерацией бесконечного цикла draw() цвет эллипсов будет чуть-чуть меняться. Также давайте добавим немного расстояния между кругами и углы наклона, изменив скорость изменения переменных c и a.
с = с + 0.06
a = a + 0.3
В качестве последнего улучшения, давайте добавим push() перед нашим кодом паттерна и pop() после. Обрамление таким способом делает код изолированным — на него не будут влиять другие объекты, которые мы будем создавать.
let r=10
let a=0
let c=20
function setup() {
background(220);
createCanvas(400, 400, WEBGL);
}
function draw() {
push()
let x = r+c*cos(a);
let y = r+c*sin(a);
fill(r,a,c);
ellipse (x,y,10,10);
c = c + 0.09
a = a + 0.8
pop()
}
В итоге у нас получится что-то такое. Самое время создать куб.
В той же функции draw(), после pop() добавим код коробки:
push()
box(200)
pop()
Сейчас коробка c размером 200 повернута к нам только одной из сторон и выглядит как просто квадрат. Чтобы оценить объем, нам надо ее развернуть. Для этого в переменные верхнего уровня к уже имеющимся там r, a,c добавим четвертую — angle с дефолтным значением 0. Чтобы этот параметр влиял на наш куб, после второго push(), но до создания коробки. добавим три функции: rotateX(), rotateY(), rotateZ() с параметром angle для каждого.
Логично, что объект с углом поворота в 0 градусов крутиться не будет, поэтому добавляем изменение значения переменной angle на 0.003 (для начала будет достаточно). В итоге мы получаем такой код:
let r=10
let a=0
let c=20
let angle = 0
function setup() {
background(220);
createCanvas(400, 400, WEBGL);
}
function draw() {
push()
let x = r+c*cos(a);
let y = r+c*sin(a);
fill(r,a,c);
ellipse (x,y,10,10);
c = c + 0.09
a = a + 0.8
pop()
push()
rotateX(angle),
rotateY(angle),
rotateZ(angle)
box(200)
angle = angle + 0.003
pop()
}
Поскольку наш фон создается только один раз (он находится в setup) — мы ловим первый баг наш куб стирает фон при повороте.
Чтобы его пофиксить, можно просто перенести background(200) внутрь функции draw() Однако теперь мы ловим другую проблему — наши круги начинают пропадать, как они это делали в начале, поскольку теперь они затираются с каждым новым циклом и возникают лишь на долю секунды из небытия.
Чтобы решить эту проблему нам необходимо создать новую переменную (например, art) без дефолтного значения в самом начале рядом с другими переменными верхнего уровня.
Внутри функции setup() мы приравниваем ее к функции createGraphics(400,400), чтобы потом использовать ее в других местах.
Убедитесь, что вы задаете те же параметры, что и при создании Canvas, чтобы соблюсти размерность.
Теперь внутри кода паттерна мы добавляем переменную art через точку к эллипсу и его свойствам (таким как fill() и др, если вы их создавали).
art.fill(r,a,c);
art.ellipse (x,y,10,10);
После этого осталось наложить паттерн на куб в качестве текстуры. Для этого уже внутри кода куба-коробки добавляем свойство texture(art). Логика следующая: мы создаем сущность графического изолированного объекта art, который вызывает функции создания паттерна с заданными свойствами, и после мы отдаем art в качестве параметра свойства текстура для куба.
let r=10
let a=0
let c=20
let angle = 0
let art
function setup() {
createCanvas(400, 400, WEBGL);
art = createGraphics(400,400);
}
function draw() {
background(220);
push()
let x = r+c*cos(a);
let y = r+c*sin(a);
art.fill(r,a,c);
art.ellipse (x,y,10,10);
c = c + 0.09
a = a + 0.8
pop()
push()
texture(art)
rotateX(angle),
rotateY(angle),
rotateZ(angle)
box(200)
angle = angle + 0.003
pop()
}
Осталось решить последнюю проблему: наш паттерн начинает проявляться с углов куба. Чтобы начать с центра, мы можем просто прибавить половину ширины и длины куба к параметру x и y, чтобы сместить центр старта отрисовки паттерна.
art.ellipse (x+200,y+200,10,10);
Если вы хотите разнообразить паттерн — можете попробовать просто изменить параметры или даже добавить несколько пересекающихся узоров, например, из другого проекта — Как воссоздать эффект муарового узора в библиотеке p5.js для новичка (быстрый гайд)
> Идея и часть исходного кода взята из статьи Nazia Fakhruddin: Creating 2D texture on a 3D shape in p5.js
> Ссылка на итоги проекта