Experimentos em Programação Criativa - 7

Essa é mais uma tentativa de reproduzir uma imagem utilizando computação criativa. Como nas últimas vezes, o objetivo dessa tentativa é de utilizar uma inspiração para aprender técnicas e conceitos novos.

Inspiração

A imagem que usarei como inspiração desta vez é a seguinte:

Skate com linhas

E o meu resultado é o seguinte:

Linhas

Implementação

Usarei novamento o editor do p5 para essa implementação, dessa vez com a modalidade WEBGL.

Passo 1

Desta vez, achei que seria mais fácil alcançar o resultado utilizando WEBGL, criando cada elemento da tela de forma 3D, facilitando assim o posicionamento de cada um em frente do outro.

Utilizar WEBGL com o p5js é bastante simples, bastando adicionar um novo parâmetro para função de criação do canvas createCanvas(400, 400, WEBGL). Criar um cubo pode ser feito de forma simples:

1
2
3
4
5
6
7
8
9
10
11
12
13
function setup() {
createCanvas(400, 400, WEBGL)
}

function draw() {
background(220)

// As funções de rotação serão apenas para debug, mas não serão necessárias para esse código
rotateX(frameCount * 0.01)
rotateY(frameCount * 0.01)

box(50, 50, 50)
}

Cubo rotacionado

Para o resultado final não usaremos as funções rotateX e rotateY, mas durante a implementação usarei em alguns momentos para depurar a posição de cada elemento da tela.

Um conceito interessante de notar, é que todos os elementos 3D adicionados em tela são posicionados no centro da tela. Portanto, quando quisermos mudar a posição de algum elemento, devemos usar as funções de transformação, como translate e rotate.

Para o resultado que estamos buscando, usaremos vários paralelepípedos posicionados lado a lado. Para isso, podemos criar um loop que adicionará os elementos e fazer um translate após isso.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const LINES_COUNT = 26
const LINE_WIDTH = 10

function setup() {
createCanvas(400, 400, WEBGL)
}

function draw() {
background(220)

// As funções de rotação serão apenas para debug, mas não serão necessárias para esse código
rotateX(frameCount * 0.01)
rotateY(frameCount * 0.01)

push()
translate(LINES_COUNT / 2 * LINE_WIDTH * -1, 0)
for (let i = 0; i < LINES_COUNT; i++) {
box(LINE_WIDTH, 300, 5)
translate(LINE_WIDTH, 0, 0)
}
pop()
}

Paralelepípedos rotacionados

Com esse resultado sem rotação:

Paralelepípedos paralelos

Transladando o eixo Z, fazemos com que cada paralelepípedo fique em posições diferentes, por onde as linhas horizontais passarão nas próximas etapas. Decidi fazer a translação duas vezes, voltando para a origem no eixo Z, para que usarmos 3 níveis diferentes (gerados de forma aleatória).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
...
function draw() {
...

push()
translate(LINES_COUNT / 2 * LINE_WIDTH * -1, 0)
for (let i = 0; i < LINES_COUNT; i++) {
const zIndex = int(random(1, 4)) * 10
translate(0, 0, zIndex)
box(LINE_WIDTH, 300, 5)
translate(LINE_WIDTH, 0, -zIndex)
}
pop()
}

Paralelepípedos paralelos

Passo 2

Antes de adicionar os paralelepípedos horizontais, vamos ajustar algumas outras coisas no código até agora. Primeiro, podemos adicionar cores.

O p5 permite várias configurações de luzes e materiais. Para o nosso caso, usaremos o emissiveMaterial(), que é usado para definir uma cor com sua força total, ignorando qualquer configuração de luz. Essa função deve ser chamada antes de renderizar algum elemento na tela, com a cor desejada.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
...
function draw() {
...

push()
translate(LINES_COUNT / 2 * LINE_WIDTH * -1, 0)
for (let i = 0; i < LINES_COUNT; i++) {
emissiveMaterial(random(100,255), random(100,255), random(100,255))
const zIndex = int(random(1, 4)) * 10
translate(0, 0, zIndex)
box(LINE_WIDTH, 300, 5)
translate(LINE_WIDTH, 0, -zIndex)
}
pop()
}

Paralelepípedos coloridos paralelos

Além disso, usamos o noStroke() para remover as bordas.

Paralelepípedos coloridos paralelos

Por fim, precisamos ajustar a camera para o modo ortogonal. Com isso, todos os objetos de mesmas dimensões terão o mesmo tamanho, independetemente da distância até a câmera. Com isso, o resultado ficará bem mais parecido com o que buscamos.

1
2
3
4
5
6
...
function setup() {
createCanvas(400, 400, WEBGL)
ortho(-width / 2, width / 2, height / 2, -height / 2, 0, 500)
}
...

Paralelepípedos coloridos ortogonais paralelos

Por mais que não pareça, se rotacionarmos o canvas conseguímos ver que o eixo Z ainda está diferente para cada elemento.

Paralelepípedos coloridos ortogonais paralelos

Parte 3

A última etapa será adicionar as linhas horizontais que atravessam as verticais. Isso pode ser feito com um loop semelhante ao anterior, apenas invertendo o desenho do cubo para horizontal, e definindo a cor padrão como preto.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
...

function draw() {
...

emissiveMaterial(0, 0, 0)

translate(-5, -165, 5)
for (let i = 0; i < 10; i++) {
const zIndex = int(random(1, 4)) * 10
translate(0, 30, zIndex)
box(300, LINE_WIDTH, 5)
translate(0, 0, -zIndex)
}
}

Paralelepípedos coloridos ortogonais paralelos

Como estamos adicionando os elementos aleatoriamente no eixo Z, conseguimos ver as faixas horizontais passando em frente e atrás das verticais.

Por fim adicionamos algumas faixas verticais para conectar as horizontais, e o resultado está pronto.

1
2
3
4
5
6
7
8
9
10
11
12
13
function draw() {
...

for (let i = 0; i < 10; i++) {
const zIndex = int(random(1, 4)) * 10
translate(0, 30, zIndex)
box(300, LINE_WIDTH, 5)
if (i === 9) break
translate(i % 2 === 0 ? 145 : -145, 15, 0)
box(LINE_WIDTH, 30, 5)
translate(i % 2 === 0 ? -145 : 145, -15, -zIndex)
}
}

Paralelepípedos coloridos ortogonais paralelos

Próximos passos

Algumas ideias para um próximo passo seria brincar com o tamanho e larguras das faixas, além de alterar mais as formas como o eixo Z é trabalhado, tentando alcançar algo mais entrelaçado.

Código completo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
const LINES_COUNT = 26
const LINE_WIDTH = 10

function setup() {
createCanvas(400, 400, WEBGL)
ortho(-width / 2, width / 2, height / 2, -height / 2, 0, 500)
}

function draw() {
background(220)

noLoop()
noStroke()

push()
translate(LINES_COUNT / 2 * LINE_WIDTH * -1, 0)
for (let i = 0; i < LINES_COUNT; i++) {
emissiveMaterial(random(100,255), random(100,255), random(100,255))
const zIndex = int(random(1, 4)) * 10
translate(0, 0, zIndex)
box(LINE_WIDTH, 300, 5)
translate(LINE_WIDTH, 0, -zIndex)
}
pop()

emissiveMaterial(0, 0, 0)

translate(-5, -165, 5)
for (let i = 0; i < 10; i++) {
const zIndex = int(random(1, 4)) * 10
translate(0, 30, zIndex)
box(300, LINE_WIDTH, 5)
if (i === 9) break
translate(i % 2 === 0 ? 145 : -145, 15, 0)
box(LINE_WIDTH, 30, 5)
translate(i % 2 === 0 ? -145 : 145, -15, -zIndex)
}
}