Наложение 2d-текстуры на 3d-объект с использованием p5.js (часть 2 — наложение паттерна на куб)

—

от автора

image

Это вторая часть проекта, в которой мы накладываем динамический 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() } 

image

В итоге у нас получится что-то такое. Самое время создать куб.

В той же функции 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) — мы ловим первый баг наш куб стирает фон при повороте.

image

Чтобы его пофиксить, можно просто перенести 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() }

image

Осталось решить последнюю проблему: наш паттерн начинает проявляться с углов куба. Чтобы начать с центра, мы можем просто прибавить половину ширины и длины куба к параметру x и y, чтобы сместить центр старта отрисовки паттерна.

art.ellipse (x+200,y+200,10,10);

image

Если вы хотите разнообразить паттерн — можете попробовать просто изменить параметры или даже добавить несколько пересекающихся узоров, например, из другого проекта — Как воссоздать эффект муарового узора в библиотеке p5.js для новичка (быстрый гайд)

→ Идея и часть исходного кода взята из статьи Nazia Fakhruddin: Creating 2D texture on a 3D shape in p5.js

→ Ссылка на итоги проекта

ссылка на оригинал статьи https://habr.com/ru/post/505402/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *